Run npm i @bellese/angular-design-system
to install
Step 2 - Installing CMS Design System
Run npm i @cmsgov/design-system-core
and
npm i @cmsgov/design-system-layout
Once you have downloaded both packages, copy the design system's fonts
, images
, and dist
directories (dist
is located inside of the design-system-core
design-system-layout
directories) inside of @cms/gov
in your application's node_modules
.
Paste these directories into your Angular application's assets
folder.
File structure:
├── assets/
├── css/
├── images/
└── fonts/
└── dist/
├── layout/
└── dist/
Reference the styles in .angular-cli.json
"styles": [
"styles.css",
"assets/css/dist/index.css",
"assets/layout/dist/index.css"
],
//app.module.ts
import { AccordionModule } from 'hqr-component-library'
import { StarModule } from 'hqr-component-library'
import { DropdownModule } from 'hqr-component-library'
import { SpinnerModule } from "./modules/spinner/spinner.module"
import { TableModule } from "./modules/table/table.module"
import { ModalModule } from "./modules/modal/modal.module"
import { ButtonModule } from "./modules/button/button.module"
import { DirectiveModule } from "./directives/directive.module" <-- DirectiveModule required for the Modal Component
import { PagingModule } from './modules/paging/paging.module'
starAmount
is the amount of stars you want displayed, this is defaulted to 5
so unless you need more than 5
you will not need to pass anything for this input. rating
is the amount of stars that will be filled. For example, a starAmount of 5 with a rating of 2 is 2 out of 5 stars. width
and height
are defaulted to 30px
, but you can pass a custom width and height be sure to detail the value as px.
//app.component.html
<app-star-rating
[width]="'10px'"
[height]="'10px'"
[starAmount]="5"
[rating]="2">
</app-star-rating>
options
expects an array of options for the dropdown menu. labelName
can be provided, but is not required.
//app.component.html
<app-dropdown
[id]="1"
[options]="options"
[labelName]="'name'">
</app-dropdown>
Spinner component sizes are small
and big
. Providing no size the spinner will appear as its default size. Colors include success
, primary
, and muted
.
<app-spinner
[dataAutoId]="1"
[hide]="false"
[inversed]="false"
[filled]="false"
[color]="'muted'"
[size]="'big'">
</app-spinner>
The table is built with many features. Each table is built to call the needed components based on the data received. Every table requires 3 items to start:
headers
: the column headers used in the tabledataRows
: the table data used in each columntableSummary
: the 508 summary to explain the purpose and describe the table. BE DETAILED
The table has been constructed to handle most cases for text overflow. We need to keep in mind that the column headers should utilize spaces or hyphens to wrap the text, the row headers should use the same format. The table data will turn into an ellipsis should the table shrink down and have text overflow. When buttons inside the data table shrink down the Asterix should still appear but there should be ellipsis where the words are. Keep in mind the Asterix is placed outside the button component so that it will always appear in a table.
tableTitle
: this input will allow a caption to show up within the table itself giving the table a namepagination
: this input will allow the option for pagination to work within the table, acceptstrue
orfalse
starRating
: this input is set tofalse
. Use this input to put a star rating on the table. All you need to pass isstarRating
with a number, the table will build the rest.index
: index assigned to tablemaxRows
: For pagination - the maximum amount of rows on a page. Defaults to 9searchText
: Used to return the exact row that matches a string passed to it. The search returns true if the word contains the string you pass -starRating
: This is specific to HQR, a way to show table ratings -linearScore
: A string to represent the linear score -reportingPeriod
: A string to represent the reporting period in the table header -highlightSearch
: A boolean to allow text to be highlighted as the searchtext is queried. -paginateNext
: This will return the next pagination page(ex: 1, 2, 3...) -total
: This is used in server side pagination, this will set the total number of rows count. Works with paginateNext. -tableRowsFiltered
: This will return the total amount of rows in the table
<app-table
[maxRows]="10"
[index]="1"
[tableTitle]="'Table Title'"
[headers]="headers"
[dataRows]="dataRows"
[paginate]="true"
[starRating]="3"
[highlightSearch]="true"
[tableSummary]="'A table summary for screen readers'">
</app-table>
The data provided to headers
in the table component has to include header
and prop
. These two keys are used to label the table header and to put the data in the right place by the prop. prop
has to match the same prop
used for the data elements. If it doesn’t match the data will not show up.
Include "sort" in the el
property in order to enable sorting for that column. When enabled the header will render as a sortable header with the proper aria-label
included and a sort indicator svg indicating ascending or descending data. If "sort" is not provided, you will just get a normal header.
If you want to sort a column on load use el
sort with attr
set to asc
or des
. This will set the direction you want to load the column data filtered as.
//app.component.ts
headers = [
{
header: {
value: "Col 1",
prop: "val1", <-- must match the property value in dataRows
el: "sort"
}
},
{
header: {
value: "Col 2",
prop: "val2",
el: "sort"
}
},
{
header: {
value: "Col 3",
prop: "val3",
el: "sort"
}
}
];
Table data is where the JSON structure really comes into play. The data structure has to make sure that the key value pair is really the data prop followed by an object with three parameters:
- Value -
value
is the actual value populated in the data cell - El - what helps the user choose between elements to include in the cell (ex:
link
,checkbox
,button
anddefault
) - Attr -
attr
is the class you want to pass to content to supplement the element. (ex:success
andfailure
) - href - this is used with el: link to set location of link
- instanceRef - this is a catch all key that is used with el: button. It passes any object back to the developer when a button is clicked. You can use this to open models, trigger alerts. All under the developers control or for their needs.
- disabled - use this to add the disabled attr to the element. true or false.
//app.component.ts
dataRows = [
{
val1: {
value: "100%",
el: "button",
attr: "",
instanceRef: {
key: value,
key: value
}
},
val2: {
value: "88%",
el: "link",
attr: "",
href: "./location"
},
val3: {
value: "1%",
el: "default",
attr: ""
}
}
]
In order to have headers that are displayed vertically within the table, set row_Header
as the property that defines each row.
row_Header: {
value: "12%",
el: "default"
}
The modal is essentially the button component that renders a modal component. But we are extending the option to add dynamic custom components within the modal. once you render the app-modal component with the requirements below everything should connect for you.
*remember - when you import modalModule make sure you do it as modalModule.forRoot()
Required:
Modal Title
– Title of the Modal
Modal Data
– The data to be sent to the modal body
Content
– The name of the modal button
Modal Component
– This will currently allow you to place a static component inside the modal, this component cannot receive data. When you use this you must import the component in the controller you are using to hold the modal component, then pass the export class name to the modal
Optional:
Modal Class
- This will change the width of the modal shell. This can be ds-c-dialog--narrow, ds-c-dialog--wide, ds-c-dialog--full
Modal ID
– The Id of the button that triggers the modal
Button Class
—The style to apply to the modal button
abbr
—In case you need to abbr the name of the button, mostly used inside the table
firstfocus
—The first thing that should be focused on when modal opens, use when it should be something that is not the cancel button
Modal Shell: Modal Shell is the easiest way to create your own unique modal. You’re going to need a few things to set this up. First make sure you followed the step above explaining how to modify app-root. Then we can start. You’re going to need to create a new component. Name it whatever you want. In the html you are going to start with . Add the following inputs
modalTitle
= title of modal
modalCount
= allows for multiple modals
add an output for (closeModal)
= This will track when the modal closes.
After you have this setup you have options to build the modal
headerTemplate
: Modal header
bodyTemplate
: Modal Body
footerTemplate
: Modal Footer
These templates can be referenced through Angular's template syntax.
ex: <ng-template #headerTemplate let-modalHeader="modalHeader">
In the TS file be sure to add inputs for modalTitle, modalData, and modalCount. And an output for closeModal;
Everything is handled automatically once you provide these input and outputs. Matter of fact the only one you are directly interacting with is modalData. And this is the data you need to make you modal body interactive. You should have a solid understand of how this works already.
Once you bundle this up you are good to go. Make sure you add this component as an entry component inside your app.module.ts. Now you have created your own reusable modal.
Now when you are ready to add this to a page, just go to your new page controller and import the modal component you just built and pass that component as a variable to app-modal under modalComponent. Be sure to pass in modalTitle, ID and modalData. First focus is optional but it is neseccary if you want to focus on something in the modal other than the cancel button when the modal first opens. By 508 standards the first input should be focused on. From here you are all set.
The modal is built the way it is because we have to come up with a way to destroy the modal after it has been enabled, if we dont we run into memory leak issues. This will cause the modal to take on values from a previous implementation. This will become a bigger issue if/when we start adding forms to modals.
The pagination component utilizes the ngx-pagination library. In order to paginate a list, a paginate
pipe must be added to your list items. You can also configure itemsPerPage
to display the amount of list items you'd like per page.
<ul>
<li *ngFor="let item of list | paginate: { itemsPerPage: 3, currentPage: p}">
{{item}}
</li>
</ul>
<app-paging (currentPage)="p = $event"></app-paging>
The bar graph component utilizes the ngx-charts library.
xAxisLabel (string)
: Label for xAxisyAxisLabel (string)
: Label for yAxisshowYAxisLabel (boolean)
: Will show/hide yAxis labelshowXAxisLabel (boolean)
: Will show/hide xAxix labelshowGridLines (boolean)
: Will show/hide graph grid linescolorScheme
: The color scheme of the chart.showXAxis (boolean)
: Will show/hide xAxis names and valuesshowYAxis (boolean)
: Will show/hide yAxis valuesroundEdges (boolean)
: Will make the edges of bars round/squareid (number)
: Unique id for using multiple charts on a single pagetitle (string)
: Heading for bar chartcompareBars (boolean)
: For comparing two bars. Whentrue
, if the first bar value is less than the second bar, the bar will be red.animations (boolean)
: Enable/disable animations for graphdata
: The data for the bar graph
Expected format for data
:
[
{
"name": "Facility",
"value": 2
},
{
"name": "National",
"value": 4
}
]
Expected format for colorScheme
:
colorScheme = {
domain: ["#256D34", "#D6D7D9"]
}
<div class="ds-l-container">
<app-bar-graph
[xAxisLabel]="'X AXIS LABEL'"
[yAxisLabel]="'Y AXIS LABEL'"
[showXAxisLabel]="true"
[showYAxisLabel]="true"
[showGridLines]="true"
[colorScheme]="colorScheme"
[roundDomains]="true"
[showXAxis]="true"
[showYAxis]="true"
[gradient]="false"
[roundEdges]="false"
[id]="1"
[title]="'Group Score Comparison'"
[compareBars]="true"
[barPadding]="20"
[animations]="true"
[data]="chartData">
</app-bar-graph>
</div>
Required inputs:
index
: This will allow you find the unique checkbox within the whole groupvalue
: Value is what the checkbox represents when clicked, and is read by screen readersgroupName
: The name that each checkbox will fall under, to help you scope this group
Operating similarly to button, Checkbox is a reusable component, but there are currently limitations. Design.CMS.gov has a component for a checkbox. Styling looks great but you can’t capture the event and have it operate as expected with html, more details to come. Currently, no event emitted because we may need to scope this out with raw javascript and grab the set by the id and reference the checked prototype.
<app-check-box
[index]="1"
[value]="Checkbox_value"
[groupName]="Checkbox_groupName">
</app-check-box>
The circle graph component utilizes the ngx-charts library.
Since we currently see this graph only being used for the error summary page we built it specific to this use case. As such, some of the layout is presented to satisfy this requirement,but you should still be able to build a normal circle graph with no extra frills beyond what is given. The layout stays as is.
Since it is built for error summary we needed to capture the center text of the circle graph. In order to do this we are taking the total getting the percent of accepted out of this. The capture the accepted you must pass an extra key of “attr” in the object, this key must equal “rate” from here the application will grab the value of the object with attr = rate and divide it over the total. We went this route in case you want to get a different rate instead of accepted, now you can choose by giving the attr of rate.
Required:
data
– the data that’s passed to the graph
Optional:
scheme
: A color scheme for the graph to absorbanimations
: True or false, will the graph have animations or notlabels
: this will allow for labels to show along the graph, BooleantrimLabels
: Boolean, this will cut the labels off if they’re too long and turn them into ellipsesmaxLabelLength
: This is required to be used along withtrimLabels
(determines how many characters you want each label to be)
explodeSlices
– This just makes the circle graph slices pop out, Booleandoughnut
– Boolean, allows the graph to have a hole in the center- Required:
arcWidth
- Required:
arcWidth
– This is the thickness of the doughnut where 1 is full width and .1 is a tiny widthgradient
– Boolean, gradient or nottoolTipDisabled
– Boolean, removes the ability to mouseover the graph and show labelstitle
– Text to go under the circle graphtableSummary
– Attribute in table to describe what the table legend is aboutlegendTitle
– Title of the table legendlegendDate
– Date for the table legendfirstColumnName
– the first column is named total, but if you want to specify what total you can pass a string. Ex: total files
<app-circle-graph
[data]="chartData">
</app-circle-graph>
chartData = [
{
"name": "Facility",
"value": 458
},
{
"name": "National",
"value": 101
}
]
The line graph was built from utilizing the ngx-charts library
Required:
data
– the data to render the graphxLabel
– the x axis labelyLabel
– the y axis label
Optional:
colorScheme
– used to layout color scheme for linesanimations
– add animations or notgridlines
– add gridlines or notroundDomain
– round the y axis to the next levelxAxis
– show or hide xyAxis
– show or hide yshowXLabel
– show or hide just the x labelshowYLabel
– show or hide just the y labelxAxisTickFormatting
– format the string for x axisyAxisTickFormatting
– format the string for y axistoolTip
– add a tooltip on mouseover
<app-line-graph
[data]="lineGraphData"
[xLabel]="'X Axis Label'"
[yLabel]="'Y Axis Label'">
</app-line-graph>
[
{
"name": "A",
"series": [
{
"name": "2010",
"value": 7300000
},
{
"name": "2011",
"value": 8940000
}
]
},
{
"name": "B",
"series": [
{
"name": "2010",
"value": 7870000
},
{
"name": "2011",
"value": 8270000
}
]
}
]
Required:
data
: The data for the card component
<div class="ds-l-container">
<app-card
[data]="cardData">
</app-card>
</div>
[
{
"title": "Card Title 1",
"summary": "Summary of card 1",
"svg_url": "assets/css/images/image.svg",
"body": [
{
"title": "Row 1",
"complete": "8/02/18",
"by": "Name",
"status": "Complete"
},
{
"title": "Row 2",
"complete": "8/02/18",
"by": "Name",
"status": "Complete"
},
{
"title": "Row 3",
"complete": "4/02/18",
"by": "Name,
"status": "Unavailable",
"lastUpload": "04/02/18"
},
{
"title": "Row 4",
"complete": "4/15/18",
"by": "Name",
"status": "Unavailable",
"lastUpload": "04/15/18"
}
],
"footer": [
{
"date": "4/15/18",
"status": "In Progress"
}
]
},
{
"title": "Card Title 2",
"summary": "Summary for card 2",
"svg_url": "assets/css/images/image.svg",
"body": [
{
"title": "Row 1",
"complete": "8/02/18",
"by": "Name",
"status": "Complete"
},
{
"title": "Row 2",
"complete": "8/02/18",
"by": "Name",
"status": "Complete"
},
{
"title": "Row 3",
"complete": "4/02/18",
"by": "Name",
"status": "Unavailable",
"lastUpload": "04/02/18"
},
{
"title": "Row 4",
"complete": "4/15/18",
"by": "Name",
"status": "Unavailable",
"lastUpload": "04/15/18"
}
],
"footer": [
{
"date": "4/15/18",
"status": "Unavailable"
}
]
}
]
heading (string)
: Status heading of the alertvariation (string)
: Type of alert status, can beerror
,warn
, orsuccess
hideIcon (boolean)
: Hide/show the alert status iconhideClose (boolean)
: Hide/show the close button
<app-alert
[heading]="'Status heading'"
[variation]="'warn'"
[hideIcon]="false"
[hideClose]="false">
Content of the alert <-- Alert content goes here
</app-alert>
When using accordion, use app-accordion to keep communicating with the other panels. For example if you open one panel the other will close. If you want the panels to open independent of each other dont wrap the panels in app-accordion.
There are no inputs.
<app-accordion>
<app-panel></app-panel>
<app-panel></app-panel>
<app-panel></app-panel>
</app-accordion>
title
: Use to name each accordion panelextTitle
: Use to add supplimental information to each panel. Goes to the right of the main title and inline with it.extTitleClass
: Use this to add different classes to change the look and feel of each ext titleexpand
: Use this only to set a main panel to open on load. For example if I want the first panel to be set to open I will add expand=true to panel.openAll
: Same concept as expand. Use this input on all panels to make each one open. Its optional: we use it when a search is open on the panels, we set openAll to true on each panel.
title
: Use to name each accordion panelid
: Use this to give the button an iddescription
: Use this to put text beneath the titlebuttonClick
: this will be emitted out so you have control over the eventariaLabel
: This will be for adding an aria label.
This component will span the width of the screen, you will need to put it in a grid layout to organize it on the page
Furthermore there is an option to add any content on the right side of the box. You will see that in the example of the img tag inside the app ticket component below.
<app-ticket
[id]="'ticketComp'"
[title]="'eCQM'"
[description]="'Track status and validation of EHR patients.'"
[ariaLabel]="'place holder for aria label'"
(buttonClick)="announce($event)">
<img src="../assets/images/performance.svg" alt="Place holder image">
</app-ticket>
Required:
userInput
: This is a required output that will tell you what the user entered
Optional:
title
: This is label name for the componentshowReset
: Boolean value that shows a reset button in bottom right cornerplaceHolder
: This is text that is prepopulated in a field for user helpid
: A unique identifier for the input fieldinputClass
: A custom class for the input fieldlabelClass
: A custom class for label elementhint
: A message above the search field to help guide the userhintClass
: A custom class for the hint divdataAutoId
: Use this id for testing purposesreset
: This is a boolean true or false, if true it will reset the field, false will do nothingchanges
: This tracks changes for you, this will return false, until you send reset. Without this you can only reset once.
This component will span the width of the screen, you will need to put it in a grid layout to organize it on the page. See example below
<app-search-field
[reset]='resetSearch'
showReset = true
placeHolder='Search'
title='Search Field'
id='tableSearch'
inputClass='searchInput'
labelClass='searchLabel'
hint='Enter a search here'
hintClass='searchHint'
dataAutoId='testingID'
(userInput)='announce($event)'
(changes)="resetSearch = $event">
</app-search-field>