@cdc/core 4.24.10 → 4.24.12-2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. package/components/AdvancedEditor/AdvancedEditor.tsx +17 -13
  2. package/components/Alert/components/Alert.tsx +39 -8
  3. package/components/DataTable/DataTable.tsx +31 -10
  4. package/components/DataTable/DataTableStandAlone.tsx +3 -3
  5. package/components/DataTable/components/ExpandCollapse.tsx +1 -1
  6. package/components/DataTable/components/SortIcon/sort-icon.css +15 -0
  7. package/components/DataTable/data-table.css +4 -22
  8. package/components/DataTable/helpers/boxplotCellMatrix.tsx +19 -14
  9. package/components/DataTable/helpers/getChartCellValue.ts +25 -7
  10. package/components/EditorPanel/ColumnsEditor.tsx +81 -36
  11. package/components/EditorPanel/DataTableEditor.tsx +62 -56
  12. package/components/EditorPanel/FieldSetWrapper.tsx +2 -2
  13. package/components/EditorPanel/Inputs.tsx +26 -16
  14. package/components/EditorPanel/VizFilterEditor/VizFilterEditor.tsx +55 -56
  15. package/components/Filters/Filters.tsx +42 -38
  16. package/components/Filters/helpers/handleSorting.ts +5 -0
  17. package/components/Footnotes/FootnotesStandAlone.tsx +17 -4
  18. package/components/Layout/components/Sidebar/components/sidebar.styles.scss +0 -4
  19. package/components/Layout/components/Visualization/visualizations.scss +1 -1
  20. package/components/Legend/Legend.Gradient.tsx +50 -35
  21. package/components/Loader/Loader.tsx +10 -5
  22. package/components/MultiSelect/MultiSelect.tsx +56 -33
  23. package/components/MultiSelect/multiselect.styles.css +20 -7
  24. package/components/NestedDropdown/NestedDropdown.tsx +55 -32
  25. package/components/NestedDropdown/nesteddropdown.styles.css +26 -13
  26. package/components/Table/Table.tsx +102 -34
  27. package/components/Table/components/Row.tsx +1 -1
  28. package/components/_stories/DataTable.stories.tsx +14 -0
  29. package/components/_stories/Filters.stories.tsx +57 -0
  30. package/components/_stories/_mocks/DataTable/no-data.json +108 -0
  31. package/components/inputs/{InputToggle.jsx → InputToggle.tsx} +35 -29
  32. package/components/ui/Icon.tsx +19 -6
  33. package/dist/cove-main.css +26 -57
  34. package/dist/cove-main.css.map +1 -1
  35. package/helpers/DataTransform.ts +2 -1
  36. package/helpers/addValuesToFilters.ts +22 -8
  37. package/helpers/cove/{number.js → number.ts} +25 -11
  38. package/helpers/coveUpdateWorker.ts +1 -1
  39. package/helpers/fetchRemoteData.js +32 -37
  40. package/helpers/filterVizData.ts +2 -2
  41. package/helpers/formatConfigBeforeSave.ts +16 -0
  42. package/helpers/gatherQueryParams.ts +2 -3
  43. package/helpers/queryStringUtils.ts +16 -1
  44. package/helpers/tests/addValuesToFilters.test.ts +6 -1
  45. package/helpers/useDataVizClasses.ts +44 -21
  46. package/helpers/ver/4.24.10.ts +12 -0
  47. package/helpers/ver/versionNeedsUpdate.ts +2 -0
  48. package/helpers/viewports.ts +8 -7
  49. package/package.json +2 -2
  50. package/styles/_button-section.scss +1 -1
  51. package/styles/_global-variables.scss +9 -4
  52. package/styles/_global.scss +21 -22
  53. package/styles/_reset.scss +0 -12
  54. package/styles/filters.scss +0 -22
  55. package/styles/v2/base/_reset.scss +0 -7
  56. package/styles/v2/components/editor.scss +0 -4
  57. package/styles/v2/components/icon.scss +1 -1
  58. package/types/Axis.ts +2 -0
  59. package/types/BoxPlot.ts +5 -3
  60. package/types/Color.ts +1 -1
  61. package/types/Legend.ts +1 -2
  62. package/types/MarkupInclude.ts +1 -0
  63. package/types/Runtime.ts +3 -1
  64. package/types/Series.ts +8 -1
  65. package/types/Table.ts +1 -1
  66. package/types/Version.ts +1 -0
  67. package/types/Visualization.ts +7 -8
  68. package/types/VizFilter.ts +2 -1
  69. package/components/ui/Select.jsx +0 -30
  70. package/helpers/getGradientLegendWidth.ts +0 -15
@@ -6,7 +6,7 @@
6
6
  }
7
7
 
8
8
  .nested-dropdown {
9
- .nested-dropdown-group {
9
+ [class^='nested-dropdown-group-'] {
10
10
  list-style: none;
11
11
  }
12
12
 
@@ -15,19 +15,22 @@
15
15
  position: relative;
16
16
  display: inline-block;
17
17
  width: 100%;
18
+ padding: 0;
19
+ &::placeholder {
20
+ color: var(--darkGray);
21
+ }
18
22
  }
19
23
 
20
24
  .main-nested-dropdown-container,
21
25
  .nested-dropdown-input-container {
22
- padding: 7px 15px;
23
- background: #fff;
24
26
  border: 1px solid var(--lightGray);
25
- border-radius: 5px;
26
- width: 325px;
27
+ min-width: 200px;
27
28
  cursor: pointer;
29
+ padding: 0.375rem 0.75rem;
30
+ font-size: 1em;
28
31
  }
29
32
 
30
- .selectable-item {
33
+ [class^='selectable-item-'] {
31
34
  list-style: none;
32
35
  padding-left: 20px;
33
36
  position: relative;
@@ -45,15 +48,24 @@
45
48
  }
46
49
 
47
50
  .nested-dropdown-input-container {
51
+ display: block;
52
+ width: 100%;
53
+ border-radius: 0.25rem;
48
54
  position: relative;
49
55
  & > span.list-arrow {
56
+ color: var(--mediumGray);
50
57
  position: absolute;
51
- font-size: 10px;
52
- top: 4px;
53
- right: 12px;
54
- padding: 9px;
55
- background: #fff;
56
- pointer-events: none;
58
+ top: 9px;
59
+ right: 1px;
60
+ float: right;
61
+ }
62
+
63
+ &.disabled {
64
+ background-color: var(--lightestGray);
65
+ & > :is(input) {
66
+ color: var(--darkGray);
67
+ background-color: var(--lightestGray);
68
+ }
57
69
  }
58
70
  }
59
71
 
@@ -62,7 +74,8 @@
62
74
  overflow-y: scroll;
63
75
  position: absolute;
64
76
  z-index: 3;
65
- width: 325px;
77
+ min-width: 200px;
78
+ background: white;
66
79
  }
67
80
 
68
81
  .hide {
@@ -4,9 +4,11 @@ import GroupRow from './components/GroupRow'
4
4
  import { CellMatrix, GroupCellMatrix } from './types/CellMatrix'
5
5
  import { RowType } from './types/RowType'
6
6
  import { PreliminaryDataItem } from '@cdc/chart/src/types/ChartConfig'
7
+ import _ from 'lodash'
7
8
 
8
9
  type TableProps = {
9
10
  childrenMatrix: CellMatrix | GroupCellMatrix
11
+ noData?: boolean
10
12
  tableName: string
11
13
  caption: string
12
14
  stickyHeader?: boolean
@@ -27,46 +29,112 @@ type TableProps = {
27
29
 
28
30
  type Position = 'sticky'
29
31
 
30
- const Table = ({ childrenMatrix, tableName, caption, stickyHeader, headContent, tableOptions, wrapColumns, hasRowType, fontSize, viewport, preliminaryData }: TableProps) => {
32
+ const Table = ({
33
+ childrenMatrix,
34
+ noData,
35
+ tableName,
36
+ caption,
37
+ stickyHeader,
38
+ headContent,
39
+ tableOptions,
40
+ wrapColumns,
41
+ hasRowType,
42
+ fontSize,
43
+ viewport,
44
+ preliminaryData
45
+ }: TableProps) => {
31
46
  const headStyle = stickyHeader ? { position: 'sticky' as Position, top: 0, zIndex: 2 } : {}
32
47
  const isGroupedMatrix = !Array.isArray(childrenMatrix)
33
48
 
34
49
  return (
35
50
  <table {...tableOptions}>
36
51
  <caption className='visually-hidden'>{caption}</caption>
37
- <thead style={headStyle}>{headContent}</thead>
38
- <tbody>
39
- {isGroupedMatrix
40
- ? Object.keys(childrenMatrix).flatMap(groupName => {
41
- let colSpan = 0
42
- const rows = childrenMatrix[groupName].map((row, i) => {
43
- colSpan = row.length
44
- const key = `${tableName}-${groupName}-row-${i}`
45
- return <Row preliminaryData={preliminaryData} key={key} rowKey={key} childRow={row} wrapColumns={wrapColumns} cellMinWidth={tableOptions.cellMinWidth} fontSize={fontSize} viewport={viewport} />
46
- })
47
- return [<GroupRow label={groupName} colSpan={colSpan} key={`${tableName}-${groupName}`} />, ...rows]
48
- })
49
- : childrenMatrix.map((childRow, i) => {
50
- let childRowCopy = [...childRow]
51
- let rowType = undefined
52
- if (hasRowType) rowType = childRowCopy.shift()
53
- const key = `${tableName}-row-${i}`
54
- if (rowType === undefined) {
55
- return <Row preliminaryData={preliminaryData} key={key} rowKey={key} childRow={childRow} wrapColumns={wrapColumns} cellMinWidth={tableOptions.cellMinWidth} fontSize={fontSize} viewport={viewport} />
56
- } else {
57
- switch (rowType) {
58
- case RowType.row_group:
59
- return <GroupRow label={childRowCopy[0]} colSpan={childRowCopy.length} key={key} />
60
- case RowType.total:
61
- return <Row preliminaryData={preliminaryData} key={key} rowKey={key} childRow={childRowCopy} isTotal={true} wrapColumns={wrapColumns} cellMinWidth={tableOptions.cellMinWidth} fontSize={fontSize} viewport={viewport} />
62
- case RowType.row_group_total:
63
- return <GroupRow label={childRowCopy[0]} colSpan={1} key={key} data={childRowCopy.slice(1)} />
64
- default:
65
- return <Row preliminaryData={preliminaryData} key={key} rowKey={key} childRow={childRowCopy} wrapColumns={wrapColumns} cellMinWidth={tableOptions.cellMinWidth} fontSize={fontSize} viewport={viewport} />
66
- }
67
- }
68
- })}
69
- </tbody>
52
+ {noData ? (
53
+ <tr>
54
+ <td className='py-5 text-center'>No Data</td>
55
+ </tr>
56
+ ) : (
57
+ <>
58
+ <thead style={headStyle}>{headContent}</thead>
59
+ <tbody>
60
+ {isGroupedMatrix
61
+ ? Object.keys(childrenMatrix).flatMap(groupName => {
62
+ let colSpan = 0
63
+ const rows = childrenMatrix[groupName].map((row, i) => {
64
+ colSpan = row.length
65
+ const key = `${tableName}-${groupName}-row-${i}`
66
+ return (
67
+ <Row
68
+ preliminaryData={preliminaryData}
69
+ key={key}
70
+ rowKey={key}
71
+ childRow={row}
72
+ wrapColumns={wrapColumns}
73
+ cellMinWidth={tableOptions.cellMinWidth}
74
+ fontSize={fontSize}
75
+ viewport={viewport}
76
+ />
77
+ )
78
+ })
79
+ return [<GroupRow label={groupName} colSpan={colSpan} key={`${tableName}-${groupName}`} />, ...rows]
80
+ })
81
+ : childrenMatrix.map((childRow, i) => {
82
+ let childRowCopy = [...childRow]
83
+ let rowType = undefined
84
+ if (hasRowType) rowType = childRowCopy.shift()
85
+ const key = `${tableName}-row-${i}`
86
+ if (rowType === undefined) {
87
+ return (
88
+ <Row
89
+ preliminaryData={preliminaryData}
90
+ key={key}
91
+ rowKey={key}
92
+ childRow={childRow}
93
+ wrapColumns={wrapColumns}
94
+ cellMinWidth={tableOptions.cellMinWidth}
95
+ fontSize={fontSize}
96
+ viewport={viewport}
97
+ />
98
+ )
99
+ } else {
100
+ switch (rowType) {
101
+ case RowType.row_group:
102
+ return <GroupRow label={childRowCopy[0]} colSpan={childRowCopy.length} key={key} />
103
+ case RowType.total:
104
+ return (
105
+ <Row
106
+ preliminaryData={preliminaryData}
107
+ key={key}
108
+ rowKey={key}
109
+ childRow={childRowCopy}
110
+ isTotal={true}
111
+ wrapColumns={wrapColumns}
112
+ cellMinWidth={tableOptions.cellMinWidth}
113
+ fontSize={fontSize}
114
+ viewport={viewport}
115
+ />
116
+ )
117
+ case RowType.row_group_total:
118
+ return <GroupRow label={childRowCopy[0]} colSpan={1} key={key} data={childRowCopy.slice(1)} />
119
+ default:
120
+ return (
121
+ <Row
122
+ preliminaryData={preliminaryData}
123
+ key={key}
124
+ rowKey={key}
125
+ childRow={childRowCopy}
126
+ wrapColumns={wrapColumns}
127
+ cellMinWidth={tableOptions.cellMinWidth}
128
+ fontSize={fontSize}
129
+ viewport={viewport}
130
+ />
131
+ )
132
+ }
133
+ }
134
+ })}
135
+ </tbody>
136
+ </>
137
+ )}
70
138
  </table>
71
139
  )
72
140
  }
@@ -19,7 +19,7 @@ const Row: FC<RowProps> = props => {
19
19
  const whiteSpace = wrapColumns ? 'unset' : 'nowrap'
20
20
  const minWidth = cellMinWidth + 'px'
21
21
  const fontSizes = { small: 16, medium: 18, large: 20 }
22
- const cellFontSize = ['sm', 'xs', 'xxs'].includes(viewport) ? '11px' : `${fontSizes[fontSize]}px`
22
+ const cellFontSize = ['xs', 'xxs'].includes(viewport) ? '12px' : `${fontSizes[fontSize]}px`
23
23
 
24
24
  return (
25
25
  <tr>
@@ -2,6 +2,7 @@ import { Meta, StoryObj } from '@storybook/react'
2
2
 
3
3
  import DataTable from '../DataTable'
4
4
  import './styles.scss'
5
+ import NoDataConfig from './_mocks/DataTable/no-data.json'
5
6
  import Example_1 from './_mocks/dashboard_no_filter.json'
6
7
  import CityStateExample from './_mocks/example-city-state.json'
7
8
  import { displayGeoName } from '@cdc/map/src/helpers/displayGeoName'
@@ -62,6 +63,19 @@ export const Grouped: Story = {
62
63
  }
63
64
  }
64
65
 
66
+ const noDataData = NoDataConfig.datasets['dev-8931-hide-markup-include.csv'].data
67
+ export const NoData: Story = {
68
+ args: {
69
+ config: NoDataConfig,
70
+ dataConfig: { data: noDataData },
71
+ rawData: noDataData,
72
+ runtimeData: noDataData,
73
+ expandDataTable: true,
74
+ tableTitle: 'No Data Table',
75
+ viewport: 'lg',
76
+ tabbingId: '#asdf'
77
+ }
78
+ }
65
79
  export const RowType: Story = {
66
80
  args: {
67
81
  config: {
@@ -0,0 +1,57 @@
1
+ import { Meta, StoryObj } from '@storybook/react'
2
+
3
+ import Filters from '../Filters'
4
+ import { VizFilter } from '../../types/VizFilter'
5
+ import { faker } from '@faker-js/faker'
6
+ import _ from 'lodash'
7
+ import { Visualization } from '../../types/Visualization'
8
+
9
+ const meta: Meta<typeof Filters> = {
10
+ title: 'Components/Molecules/Visualization Filters',
11
+ component: Filters
12
+ }
13
+
14
+ type Story = StoryObj<typeof Filters>
15
+
16
+ faker.seed(123)
17
+
18
+ const animalData = _.times(7, () => ({ bear: faker.animal.bear(), cat: faker.animal.cat(), cow: faker.animal.cow() }))
19
+ const generateFilters = filterStyle => {
20
+ return ['bear', 'cat', 'cow'].map(columnName => {
21
+ return {
22
+ filterStyle,
23
+ label: columnName.toUpperCase(),
24
+ columnName
25
+ } as VizFilter
26
+ })
27
+ }
28
+
29
+ const generateConfig = filterStyle =>
30
+ ({
31
+ args: {
32
+ config: {
33
+ filters: generateFilters(filterStyle),
34
+ data: animalData
35
+ } as Visualization,
36
+ filteredData: animalData,
37
+ setFilteredData: () => {},
38
+ setConfig: () => {},
39
+ exclusions: []
40
+ }
41
+ } as Story)
42
+
43
+ export const Dropdown: Story = generateConfig('dropdown')
44
+
45
+ export const DropdownBar: Story = generateConfig('dropdown bar')
46
+
47
+ export const MultiSelect: Story = generateConfig('multi-select')
48
+
49
+ export const NestedDropdown: Story = generateConfig('nested-dropdown')
50
+
51
+ export const Pill: Story = generateConfig('pill')
52
+
53
+ export const Tab: Story = generateConfig('tab')
54
+
55
+ export const TabBar: Story = generateConfig('tab bar')
56
+
57
+ export default meta
@@ -0,0 +1,108 @@
1
+ {
2
+ "dashboard": {
3
+ "theme": "theme-blue",
4
+ "sharedFilters": []
5
+ },
6
+ "rows": [
7
+ {
8
+ "columns": [
9
+ {
10
+ "width": 12,
11
+ "widget": "table1730380781112"
12
+ }
13
+ ],
14
+ "uuid": 1730380998726
15
+ }
16
+ ],
17
+ "visualizations": {
18
+ "table1730380781112": {
19
+ "filters": [],
20
+ "filterBehavior": "Filter Change",
21
+ "newViz": false,
22
+ "openModal": true,
23
+ "uid": "table1730380781112",
24
+ "type": "table",
25
+ "table": {
26
+ "label": "Data Table",
27
+ "show": true,
28
+ "showDownloadUrl": false,
29
+ "showVertical": true,
30
+ "expanded": true,
31
+ "collapsible": true
32
+ },
33
+ "columns": {
34
+ "additionalColumn1": {
35
+ "label": "New Column",
36
+ "dataTable": true,
37
+ "tooltips": false,
38
+ "prefix": "",
39
+ "suffix": "",
40
+ "forestPlot": false,
41
+ "startingPoint": "0",
42
+ "forestPlotAlignRight": false,
43
+ "roundToPlace": 0,
44
+ "commas": false,
45
+ "showInViz": false,
46
+ "forestPlotStartingPoint": 0
47
+ },
48
+ "Class": {
49
+ "name": "Class",
50
+ "dataTable": false
51
+ },
52
+ "Data_Type": {
53
+ "name": "Data_Type",
54
+ "dataTable": false
55
+ },
56
+ "Question": {
57
+ "name": "Question",
58
+ "dataTable": false
59
+ },
60
+ "Response": {
61
+ "name": "Response",
62
+ "dataTable": false
63
+ },
64
+ "Topic": {
65
+ "name": "Topic",
66
+ "dataTable": false
67
+ }
68
+ },
69
+ "dataFormat": {},
70
+ "visualizationType": "table",
71
+ "dataDescription": {
72
+ "horizontal": false,
73
+ "series": false
74
+ },
75
+ "dataKey": "dev-8931-hide-markup-include.csv"
76
+ }
77
+ },
78
+ "table": {
79
+ "label": "Data Table",
80
+ "show": true,
81
+ "showDownloadUrl": false,
82
+ "showDownloadLinkBelow": true,
83
+ "showVertical": true
84
+ },
85
+ "newViz": true,
86
+ "datasets": {
87
+ "dev-8931-hide-markup-include.csv": {
88
+ "data": [],
89
+ "dataFileSize": 10023,
90
+ "dataFileName": "dev-8931-hide-markup-include.csv",
91
+ "dataFileSourceType": "file",
92
+ "dataFileFormat": "CSV",
93
+ "preview": true
94
+ }
95
+ },
96
+ "isResponsiveTicks": false,
97
+ "type": "dashboard",
98
+ "barThickness": "0.37",
99
+ "xAxis": {
100
+ "type": "categorical",
101
+ "size": 75,
102
+ "maxTickRotation": 45,
103
+ "labelOffset": 0
104
+ },
105
+ "runtime": {},
106
+ "version": "4.24.10",
107
+ "uuid": 1730380778769
108
+ }
@@ -1,9 +1,29 @@
1
1
  import React, { useState, useEffect } from 'react'
2
- import PropTypes from 'prop-types'
3
2
 
4
3
  import '../../styles/v2/components/input/index.scss'
5
4
 
6
- const InputSlider = ({
5
+ type InputSliderProps = {
6
+ /** Add label to the input field */
7
+ label: string
8
+ /** Select the preferred display style of the slider */
9
+ sliderType?: 'flat' | 'block' | 'pill' | '3d'
10
+ /** Select the preferred size of the slider */
11
+ size?: 'small' | 'medium' | 'large'
12
+ /** Select the preferred color for the slider when active */
13
+ activeColor?: string
14
+ /** Parent key value of nested target config option */
15
+ section?: string
16
+ /** Child key value of nested target config option */
17
+ subsection?: string
18
+ /** Key value of targeted config option */
19
+ fieldName?: string
20
+ /** Prop drill down of the updateField function */
21
+ updateField?: Function
22
+ /** Current value of the input, usually the current config option value */
23
+ value: boolean
24
+ }
25
+
26
+ const InputSlider: React.FC<InputSliderProps> = ({
7
27
  label,
8
28
  sliderType = 'flat',
9
29
  size = 'medium',
@@ -60,38 +80,24 @@ const InputSlider = ({
60
80
  return (
61
81
  <div className='input-group'>
62
82
  {label && <label>{label}</label>}
63
- <div className={'cove-input__slider' + (size === 'small' ? '--small' : size === 'large' ? '--large' : '') + sliderTypeClass() + (value ? ' active' : '')} onClick={clickHandler}>
83
+ <div
84
+ className={
85
+ 'cove-input__slider' +
86
+ (size === 'small' ? '--small' : size === 'large' ? '--large' : '') +
87
+ sliderTypeClass() +
88
+ (value ? ' active' : '')
89
+ }
90
+ onClick={clickHandler}
91
+ >
64
92
  <div className='cove-input__slider-button' />
65
- <div className='cove-input__slider-track' style={value && activeColor ? { backgroundColor: activeColor } : null} />
93
+ <div
94
+ className='cove-input__slider-track'
95
+ style={value && activeColor ? { backgroundColor: activeColor } : null}
96
+ />
66
97
  <input className='cove-input--hidden' type='checkbox' name={name()} checked={value || false} readOnly />
67
98
  </div>
68
99
  </div>
69
100
  )
70
101
  }
71
102
 
72
- InputSlider.propTypes = {
73
- /** Add label to the input field */
74
- label: PropTypes.string,
75
- /** Select the preferred display style of the slider */
76
- sliderType: PropTypes.oneOf(['flat', 'block', 'pill', '3d']),
77
- /** Select the preferred size of the slider */
78
- size: PropTypes.oneOf(['small', 'medium', 'large']),
79
- /** Select the preferred color for the slider when active */
80
- activeColor: PropTypes.string,
81
- /** Parent key value of nested target config option
82
- *
83
- * (optional)*/
84
- section: PropTypes.string,
85
- /** Child key value of nested target config option
86
- *
87
- * (optional, requires `section` value)*/
88
- subsection: PropTypes.string,
89
- /** Key value of targeted config option */
90
- fieldName: PropTypes.string,
91
- /** Prop drill down of the updateField function */
92
- updateField: PropTypes.func,
93
- /** Current value of the input, usually the current config option value */
94
- stateValue: PropTypes.object
95
- }
96
-
97
103
  export default InputSlider
@@ -88,15 +88,24 @@ type IconProps = {
88
88
  /* Returns icon data as plain svg */
89
89
  base?: boolean
90
90
  /* Sets alt text for the icon */
91
- alt: string
91
+ alt?: string
92
92
  /* Override the width of the icon (scales height proportionally)*/
93
93
  size?: number
94
94
  /* Override the color of the icon */
95
95
  color?: string
96
- style?: string
96
+ style?: object
97
+ className?: string // className attribute will be ignored.
97
98
  }
98
99
 
99
- const Icon: React.FC<IconProps> = ({ display = '', base = undefined, alt = '', size = undefined, color = undefined, style = undefined, ...attributes }) => {
100
+ const Icon: React.FC<IconProps> = ({
101
+ display = '',
102
+ base = undefined,
103
+ alt = '',
104
+ size = undefined,
105
+ color = undefined,
106
+ style = undefined,
107
+ ...attributes
108
+ }) => {
100
109
  const IconObj = iconHash[display] || null
101
110
 
102
111
  const filteredAttrs = { ...attributes }
@@ -112,10 +121,14 @@ const Icon: React.FC<IconProps> = ({ display = '', base = undefined, alt = '', s
112
121
  return (
113
122
  <>
114
123
  {base ? (
115
- <IconObj title={alt} />
124
+ <IconObj title={alt || display} />
116
125
  ) : (
117
- <span className={`cove-icon${attributes.className ? ' ' + attributes.className : ''}`} style={styles} {...filteredAttrs}>
118
- <IconObj title={alt} />
126
+ <span
127
+ className={`cove-icon${attributes.className ? ' ' + attributes.className : ''}`}
128
+ style={styles}
129
+ {...filteredAttrs}
130
+ >
131
+ <IconObj title={alt || display} />
119
132
  </span>
120
133
  )}
121
134
  </>