'How to make Angular Material mat-select select list width automatic and adapt to the widest option?

Problem

I have a Angular 6.1.10 web app. I want the width of the mat-select list options to be automatic and adapt to the widest option in the options list. See the screenshot below: enter image description here

Code of the Page Showing the mat-select List

HTML Code

<div class="all-container">
  <div class="top-border"></div>
  <div class="content-container">

    <div *ngIf="statisticsPageToDisplay == 'surveyTemplateSelectionPage'">
      <div class="title">
        Select a survey to view statistics for
      </div>
      <div class="survey-templates-container">
        <div class="survey-templates-scroll-list">
          <div *ngFor="let surveyTemplate of surveyTemplates">
            <app-statistics-page-survey-template-panel (onClickEmitter)="surveyTemplatePanelClicked($event)"
              [surveyTemplate]="surveyTemplate"></app-statistics-page-survey-template-panel>
          </div>
        </div>
      </div>
    </div>

    <div *ngIf="statisticsPageToDisplay == 'surveyTemplateStatistics'">

      <div class="top-buttons-row">
        <button mat-raised-button style="float: left; margin-top: 0.75%; margin-left: 1.5%"
          (click)="selectAnotherSurveyTemplateClicked()" [color]="'primary'">
          <i class="fas fa-chevron-left"></i>
          <span style="margin-left: 0.75em !important">Select another survey</span>
        </button>
      </div>
      <div class="mat-select-formatting">
        <mat-form-field>
          <div class="team-selection-list">

            <mat-select (selectionChange)="selectedTeamOptionChange()" placeholder="Select team to view statistics for"
              [(value)]="selectedTeamOption">
              <mat-select-trigger>
                <i class="fas fa-building"></i>
                <span style="margin-left: 0.3em">
                  {{selectedTeamOption?.companyRef?.name}}, 
                </span>
                <i class="fas fa-users"></i>
                <span style="margin-left: 0.3em">
                  {{selectedTeamOption?.name}}
                </span>
              </mat-select-trigger>
              <mat-option *ngFor="let teamOption of teamOptions" [value]="teamOption">
                <i class="fas fa-building"></i>
                <span style="margin-left: 0.3em">
                  {{teamOption?.companyRef?.name}}, 
                </span>
                <i class="fas fa-users"></i>
                <span style="margin-left: 0.3em">
                  {{teamOption?.name}}
                </span>
              </mat-option>
            </mat-select>
          </div>
        </mat-form-field>
      </div>
      <div class="title-row">
        <div class="title smallest-top-margin">
          Statistics for survey {{currentStatisticsSurvey.title}} for team {{selectedTeamOption}}
        </div>
      </div>

      <div *ngIf="redDataPoints != null && yellowDataPoints != null && greenDataPoints != null">
        <app-smileys-time-line-chart [redDataPoints]="redDataPoints" [yellowDataPoints]="yellowDataPoints"
          [greenDataPoints]="greenDataPoints" (chartPointClicked)="chartPointClicked($event)">
        </app-smileys-time-line-chart>
      </div>

      <div *ngIf="selectedBarChartTime != null">
        <div class="title small-top-margin">
          Category statistics for survey {{currentStatisticsSurvey.title}} for team {{selectedTeamOption}} at
          {{selectedBarChartTime.toLocaleDateString()}}
        </div>
        <app-categories-bar-chart [categoryStatistics]="barChartStatistics"></app-categories-bar-chart>
      </div>

    </div>
    
  </div>
</div>

CSS Code

.mat-raised-button {
    height: 1.5em !important;
    line-height: 1em !important;
    margin-bottom: 0.6em !important;
    text-align: left !important; 
    padding-left: 0.5em !important;
    padding-right: 1em !important;
    margin-left: 0 !important;
}

.mat-raised-button i {
    margin-bottom: 0.1em !important;
}

.all-container {
    display: flex;
    flex-direction: column;
    justify-content: start;
    align-items: center;
    box-shadow: none !important;
}

.top-border {
    height: 1px;
    width: 100%;
    margin: auto;
    border-top: 0.75px solid rgb(225, 225, 225);
}

.content-container {
    width: 98%;
    margin: auto;
    padding-top: 5px;
    display: flex;
    flex-direction: column;
    justify-content: start;
    align-items: flex-start;
    box-shadow: none !important;
}

.top-buttons-row {
    width: 100%;
    height: auto;
    display: flex;
    flex-direction: row;
    justify-content: start;
    align-items: flex-start;
}

.title-row {
    width: 100%;
    height: auto;
    display: flex;
    flex-direction: row;
    justify-content: start;
    align-items: stretch;
}

.title {
  color: rgb(150, 150, 150);
  font-family: Verdana, Geneva, Tahoma, sans-serif;
  font-size: 1.2em;
  width: 100%;
}

.survey-templates-container {
    width: 30vw;
    height: 65vh;
    margin-top: 15px;
    background-color: white;
    border-radius: 5px;
    box-shadow: 2.5px 2.5px 10px 1px rgb(225, 225, 225);
    border: 0.5px solid rgb(220, 220, 220);
    overflow: hidden;
}

.survey-templates-scroll-list {
    width: 98%;
    height: 98%;
    overflow-x: hidden;
    overflow-y: auto;
    padding: 1%;
    display: flex;
    flex-direction: column;
    justify-content: start;
    align-items: center;
}

::ng-deep .mat-select-formatting .mat-form-field {
    margin-top: 7.5px !important;
    padding-top: 0px !important;
    padding-bottom: 5px !important;
    padding-left: 0px !important;
    padding-right: 0px !important;
    height: 2.5em !important;
    max-height: 2.5em !important;
    min-height: 2.5em !important;
    width: auto !important;
    max-width: 100% !important;
    color: rgb(125, 125, 125) !important;
}

.mat-select {
    width: 100% !important;
    min-width: 100% !important;
    max-width: 100% !important;
    cursor: pointer !important;
    color: rgb(125, 125, 125) !important;
}

.mat-select-content {
    width: 100% !important;
    min-width: 100% !important;
    max-width: 100% !important;
    cursor: pointer !important;
    padding-left: 1em !important;
    padding-right: 1em !important;
    color: rgb(125, 125, 125) !important;
}

.mat-select-placeholder {
    color: rgb(125, 125, 125) !important;
}

::ng-deep .mat-select-value {
    width: auto;
    max-width: 100% !important;
    color: rgb(125, 125, 125) !important;
}

::ng-deep .mat-select-value-text {
    color: rgb(125, 125, 125) !important;
}

.mat-select-panel {
    color: rgb(125, 125, 125) !important;
}

.mat-select-trigger {
    color: rgb(125, 125, 125) !important;
}

.mat-option {
    color: rgb(125, 125, 125) !important;
    height: 1.75em !important;
    min-height: 1.75em !important;
    max-height: 1.75em !important;
}

.mat-select:hover {
    cursor: pointer !important;
}

.mat-select:active {
    cursor: pointer !important;
}

.smallest-top-margin {
    margin-top: 7px;
}

.smaller-top-margin {
    margin-top: 10px;
}

.small-top-margin {
    margin-top: 15px;
    }

TypeScript Code

import { Component, OnInit } from '@angular/core';
import { SurveyTemplateModel } from 'src/shared_classes/models/SurveyTemplateModel';
import { AppAnswer } from 'src/shared_classes/app_models/AppAnswer';
import { Answer } from 'src/shared_classes/app_models/Answer';
import { AppQuestion } from 'src/shared_classes/app_models/AppQuestion';
import { Question } from 'src/shared_classes/app_models/Question';
import { Category } from 'src/shared_classes/app_models/Category';
import { AppCategory } from 'src/shared_classes/app_models/AppCategory';
import { SurveyTemplate } from 'src/shared_classes/app_models/SurveyTemplate';
import { PageLoadHttpService } from 'src/service/page-load-http.service';
import { StatisticsPageSurveyTemplatePanelComponent } from 'src/feature_components/statistics-page-survey-template-panel/statistics-page-survey-template-panel.component';
import { SurveyHttpServiceService } from 'src/service/survey-http-service.service';
import { GatheredTeamSurveyStatistics } from 'src/shared_classes/models/GatheredTeamSurveyStatistics';
import { OrgReference } from 'src/shared_classes/models/property_classes/OrgReference';
import { SurveyStatisticsDataPoint } from 'src/shared_classes/models/SurveyStatisticsDataPoint';
import { LineChartDataSet } from 'src/shared_classes/other_data_classes/LineChartDataSet';
import { TimeLineChartDataPoint } from 'src/shared_classes/models/TimeLineChartDataPoint';
import { CategoryBarChartStatistic } from 'src/shared_classes/models/CategoryBarChartStatistic';
import { TimePoint } from 'src/shared_classes/other_data_classes/Point';
import { Team } from 'src/shared_classes/app_models/Team';

@Component({
  selector: 'app-statistics-page',
  templateUrl: './statistics-page.component.html',
  styleUrls: ['./statistics-page.component.css']
})
export class StatisticsPageComponent implements OnInit {

  surveyTemplates: SurveyTemplate[];
  statisticsPageToDisplay: string;
  currentStatisticsSurvey: SurveyTemplate;
  teamOptions: Team[];
  selectedTeamOption: Team;
  greenDataPoints: TimeLineChartDataPoint[];
  yellowDataPoints: TimeLineChartDataPoint[];
  redDataPoints: TimeLineChartDataPoint[];
  barChartStatistics: CategoryBarChartStatistic[];
  allLoadedTeamStatistics: GatheredTeamSurveyStatistics[];
  selectedTeamStatisticsData: GatheredTeamSurveyStatistics;
  selectedBarChartTime: Date;

  constructor(private pageLoadHttpService: PageLoadHttpService,
    private surveyHttpService: SurveyHttpServiceService) { }

  ngOnInit() {
    this.statisticsPageToDisplay = "surveyTemplateSelectionPage";
    this.surveyTemplates = [];
    this.loadSurveyTemplates();
  }

  loadSurveyTemplates() {
    this.pageLoadHttpService.getSurveyTemplates().subscribe((surveyTemplModels: SurveyTemplateModel[]) => {
      this.surveyTemplates = surveyTemplModels.map(surveyTemplModel =>
        this.convertSurveyTemplateModelToSurveyTemplate(surveyTemplModel));
    });
  }

  loadSurveyStatisticsData(surveyTemplateGuid: string) {
    this.surveyHttpService.getTeamsSurveyStatisticsData(surveyTemplateGuid).subscribe((statisticsData: GatheredTeamSurveyStatistics[]) => {
      this.allLoadedTeamStatistics = statisticsData;
      this.useLoadedSurveyStatisticsData(statisticsData);
    });
  }

  useLoadedSurveyStatisticsData(statisticsData: GatheredTeamSurveyStatistics[]) {
    this.createTeamOptions(statisticsData);
    this.selectedTeamOption = this.teamOptions[0];
    this.selectedTeamStatisticsData = this.getSelectedTeamStatisticsData(statisticsData);

    this.createSmileyLineChart(this.selectedTeamStatisticsData.statisticsDataPoints);
  }

  getSelectedTeamStatisticsData(statisticsData: GatheredTeamSurveyStatistics[]): GatheredTeamSurveyStatistics {
    var selectedTeamStatisticsData: GatheredTeamSurveyStatistics;
    for (var i = 0; i < statisticsData.length; i++)
      if (statisticsData[i].teamRef.guid == this.selectedTeamOption.guid) {
        selectedTeamStatisticsData = statisticsData[i];
        break;
      }
    return selectedTeamStatisticsData;
  }

  createGreenTimeLineChartDataPoint(dataPoint: SurveyStatisticsDataPoint): TimeLineChartDataPoint {
    var timeLineChartDataPoint: TimeLineChartDataPoint = {
      score: dataPoint.greenDataPoint.score,
      greenGoalLimit: dataPoint.greenGoalLimit,
      yellowGoalLimit: dataPoint.yellowGoalLimit,
      redGoalLimit: dataPoint.redGoalLimit,
      time: dataPoint.greenDataPoint.time
    };
    return timeLineChartDataPoint;
  }

  createYellowTimeLineChartDataPoint(dataPoint: SurveyStatisticsDataPoint): TimeLineChartDataPoint {
    var timeLineChartDataPoint: TimeLineChartDataPoint = {
      score: dataPoint.yellowDataPoint.score,
      greenGoalLimit: dataPoint.greenGoalLimit,
      yellowGoalLimit: dataPoint.yellowGoalLimit,
      redGoalLimit: dataPoint.redGoalLimit,
      time: dataPoint.yellowDataPoint.time
    };
    return timeLineChartDataPoint;
  }

  createRedTimeLineChartDataPoint(dataPoint: SurveyStatisticsDataPoint): TimeLineChartDataPoint {
    var timeLineChartDataPoint: TimeLineChartDataPoint = {
      score: dataPoint.redDataPoint.score,
      greenGoalLimit: dataPoint.greenGoalLimit,
      yellowGoalLimit: dataPoint.yellowGoalLimit,
      redGoalLimit: dataPoint.redGoalLimit,
      time: dataPoint.redDataPoint.time
    };
    return timeLineChartDataPoint;
  }

  createSmileyLineChart(statisticsDataPoints: SurveyStatisticsDataPoint[]) {

    var redDataPoints: TimeLineChartDataPoint[] = [];
    var yellowDataPoints: TimeLineChartDataPoint[] = [];
    var greenDataPoints: TimeLineChartDataPoint[] = [];

    statisticsDataPoints.forEach(dataPoint => {
      redDataPoints.push(this.createRedTimeLineChartDataPoint(dataPoint));
      yellowDataPoints.push(this.createYellowTimeLineChartDataPoint(dataPoint));
      greenDataPoints.push(this.createGreenTimeLineChartDataPoint(dataPoint));
    });

    redDataPoints.forEach(redDataPoint => {
      redDataPoint.time = new Date(redDataPoint.time);
    });
    yellowDataPoints.forEach(yellowDataPoint => {
      yellowDataPoint.time = new Date(yellowDataPoint.time);
    });
    greenDataPoints.forEach(greenDataPoint => {
      greenDataPoint.time = new Date(greenDataPoint.time);
    });

    this.redDataPoints = redDataPoints;
    this.yellowDataPoints = yellowDataPoints;
    this.greenDataPoints = greenDataPoints;
  }

  createBarChart(statisticsDataPoints: SurveyStatisticsDataPoint[]) {
  }

  surveyTemplatePanelClicked(clickedPanel: StatisticsPageSurveyTemplatePanelComponent) {
    this.openStatisticsPage(clickedPanel.surveyTemplate);
  }

  openStatisticsPage(surveyTemplate: SurveyTemplate) {
    this.currentStatisticsSurvey = surveyTemplate;
    this.statisticsPageToDisplay = "surveyTemplateStatistics";
    this.loadSurveyStatisticsData(surveyTemplate.guid);
  }

  convertSurveyTemplateModelToSurveyTemplate(model: SurveyTemplateModel): SurveyTemplate {
    var appCategories: AppCategory[] = [];
    model.categories.forEach(c => {
      appCategories.push(this.convertCategoryToAppCategory(c));
    });

    var surveyTemplate: SurveyTemplate = {
      categories: appCategories,
      date: model.date,
      guid: model.guid,
      isChecked: false,
      companyGuids: model.companyGuids,
      teamGuids: model.teamGuids,
      userGuid: model.userGuid,
      title: model.title,
      info: model.info,
      defaultGreenGoalLimit: model.defaultGreenGoalLimit,
      defaultYellowGoalLimit: model.defaultYellowGoalLimit,
      defaultRedGoalLimit: model.defaultRedGoalLimit
    }
    return surveyTemplate;
  }

  convertCategoryToAppCategory(category: Category): AppCategory {
    var appQuestions: AppQuestion[] = [];
    category.questions.forEach(q => {
      appQuestions.push(this.convertQuestionToAppQuestion(q));
    });

    var appCategory: AppCategory = {
      questions: appQuestions,
      isChecked: false,
      title: category.title,
      nrGreenCategoryScores: category.nrGreenCategoryScores,
      nrYellowCategoryScores: category.nrYellowCategoryScores,
      nrRedCategoryScores: category.nrRedCategoryScores
    };

    return appCategory;
  }

  convertQuestionToAppQuestion(question: Question): AppQuestion {
    var appAnswers: AppAnswer[] = [];
    question.answers.forEach(a => {
      appAnswers.push(this.convertAnswerToAppAnswer(a));
    });

    var appQuestion: AppQuestion = {
      questionStr: question.questionStr,
      answers: appAnswers,
      pickedAnswer: null,
      isChecked: false
    };

    return appQuestion;
  }

  convertAnswerToAppAnswer(answer: Answer): AppAnswer {
    var appAnswer: AppAnswer = {
      answerStr: answer.answerStr,
      points: answer.points,
      isChecked: false
    };

    return appAnswer;
  }

  selectAnotherSurveyTemplateClicked() {
    this.statisticsPageToDisplay = "surveyTemplateSelectionPage";
  }

  createTeamOptions(gatheredTeamsSurveyStatistics: GatheredTeamSurveyStatistics[]) {
    this.teamOptions = [];
    gatheredTeamsSurveyStatistics.forEach(gtss => {
      var team = this.getStatisticsDataTeam(gtss);
      this.teamOptions.push(team);
    });
    this.sortTeamOptions();
  }

  sortTeamOptions() {
    this.teamOptions.sort(this.compareTeamNames);
  }

  compareTeamNames(a:Team, b:Team): number {
    var companyNameComparison = a.companyRef.name.toLowerCase().localeCompare(b.companyRef.name.toLowerCase());
    
    if(companyNameComparison !== 0)
      return companyNameComparison;

    return a.name.toLowerCase().localeCompare(b.name.toLowerCase());
  }

  selectedTeamOptionChange() {
    console.log("Selected team: " + this.selectedTeamOption);
    this.selectedTeamStatisticsData = this.getSelectedTeamStatisticsData(this.allLoadedTeamStatistics);
    this.createSmileyLineChart(this.selectedTeamStatisticsData.statisticsDataPoints);
    this.selectedBarChartTime = null;
  }

  chartPointClicked(timePoint: TimePoint) {
    this.updateBarChart(timePoint);
  }

  updateBarChart(timePoint: TimePoint) {
    for (var i = 0; i < this.selectedTeamStatisticsData.statisticsDataPoints.length; i++) {
      if ((new Date(this.selectedTeamStatisticsData.statisticsDataPoints[i].greenDataPoint.time)).getTime() == (new Date(timePoint.x)).getTime()) {
        this.barChartStatistics = this.selectedTeamStatisticsData.statisticsDataPoints[i].categoryBarChartStatistics;
        break;
      }
    }
    this.selectedBarChartTime = timePoint.x;
  }

  getStatisticsDataTeam(statisticsData: GatheredTeamSurveyStatistics): Team {
    return new Team (
      statisticsData.teamRef.name,
      statisticsData.teamRef.guid,
      null,
      null,
      null,
      null,
      null,
      statisticsData.companyRef,
      null,
      null,
      null,
      null,
      null,
      null,
      null
    );
  }

}

Final Thoughts and Question

I have tried lots of different CSS code and inspecting the mat-select in the Chrome browser. But nothing has worked so far. How can I make the mat-select options list width be automatic and adapt to the widest option in the list so that they don't clip?

Thanks!



Solution 1:[1]

For me, it was enough to override max-width on the following core CSS class. Do it via ViewEncapsulation.None, or via ::ng-deep. Set it to max-width: fit-content or max-width: initial. Example:

::ng-deep .my-custom-panel-class.mat-select-panel {
    ...
    max-width: fit-content;
    ...
}

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 ilya.chepurnoy