@kaizen/components 1.68.3 → 1.68.5

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 (88) hide show
  1. package/dist/cjs/Filter/FilterBar/context/FilterBarContext.cjs +13 -3
  2. package/dist/cjs/Filter/FilterBar/context/reducer/filterBarStateReducer.cjs +1 -1
  3. package/dist/cjs/Filter/FilterBar/context/reducer/setupFilterBarState.cjs +4 -0
  4. package/dist/cjs/Filter/FilterBar/context/utils/updateDependentFilters.cjs +1 -1
  5. package/dist/cjs/Filter/FilterBar/subcomponents/ClearAllButton/ClearAllButton.cjs +7 -2
  6. package/dist/cjs/Filter/FilterBar/subcomponents/ClearAllButton/ClearAllButton.module.scss.cjs +2 -1
  7. package/dist/cjs/Filter/FilterDateRangePicker/subcomponents/FilterDateRangePickerField/FilterDateRangePickerField.cjs +3 -0
  8. package/dist/cjs/Tile/subcomponents/GenericTile/GenericTile.cjs +23 -2
  9. package/dist/esm/Filter/FilterBar/context/FilterBarContext.mjs +13 -3
  10. package/dist/esm/Filter/FilterBar/context/reducer/filterBarStateReducer.mjs +1 -1
  11. package/dist/esm/Filter/FilterBar/context/reducer/setupFilterBarState.mjs +4 -0
  12. package/dist/esm/Filter/FilterBar/context/utils/updateDependentFilters.mjs +1 -1
  13. package/dist/esm/Filter/FilterBar/subcomponents/ClearAllButton/ClearAllButton.mjs +6 -2
  14. package/dist/esm/Filter/FilterBar/subcomponents/ClearAllButton/ClearAllButton.module.scss.mjs +2 -1
  15. package/dist/esm/Filter/FilterDateRangePicker/subcomponents/FilterDateRangePickerField/FilterDateRangePickerField.mjs +3 -0
  16. package/dist/esm/Tile/subcomponents/GenericTile/GenericTile.mjs +24 -3
  17. package/dist/styles.css +77 -73
  18. package/dist/types/Filter/FilterBar/context/FilterBarContext.d.ts +1 -0
  19. package/dist/types/Filter/FilterBar/context/types.d.ts +1 -0
  20. package/locales/ar.json +8 -0
  21. package/locales/bg.json +8 -0
  22. package/locales/cs.json +8 -0
  23. package/locales/cy.json +8 -0
  24. package/locales/da.json +8 -0
  25. package/locales/de.json +8 -0
  26. package/locales/el.json +8 -0
  27. package/locales/en-GB.json +8 -0
  28. package/locales/es-419.json +8 -0
  29. package/locales/es.json +8 -0
  30. package/locales/et.json +8 -0
  31. package/locales/fi.json +8 -0
  32. package/locales/fr-CA.json +8 -0
  33. package/locales/fr.json +8 -0
  34. package/locales/he.json +8 -0
  35. package/locales/hi.json +8 -0
  36. package/locales/ht.json +8 -0
  37. package/locales/hu.json +8 -0
  38. package/locales/id.json +8 -0
  39. package/locales/it.json +8 -0
  40. package/locales/ja.json +8 -0
  41. package/locales/km-KH.json +8 -0
  42. package/locales/ko.json +8 -0
  43. package/locales/lt.json +8 -0
  44. package/locales/lv.json +8 -0
  45. package/locales/mi.json +8 -0
  46. package/locales/ms.json +8 -0
  47. package/locales/nb.json +8 -0
  48. package/locales/nl.json +8 -0
  49. package/locales/pl.json +8 -0
  50. package/locales/pt-BR.json +8 -0
  51. package/locales/pt.json +8 -0
  52. package/locales/ro.json +8 -0
  53. package/locales/ru.json +8 -0
  54. package/locales/si-LK.json +8 -0
  55. package/locales/sk.json +8 -0
  56. package/locales/sr.json +8 -0
  57. package/locales/sv.json +8 -0
  58. package/locales/th.json +8 -0
  59. package/locales/tl.json +8 -0
  60. package/locales/tr.json +8 -0
  61. package/locales/uk.json +8 -0
  62. package/locales/vi.json +8 -0
  63. package/locales/zh-TW.json +8 -0
  64. package/locales/zh.json +8 -0
  65. package/package.json +1 -1
  66. package/src/Filter/FilterBar/FilterBar.spec.tsx +0 -64
  67. package/src/Filter/FilterBar/_docs/FilterBar.spec.stories.tsx +249 -0
  68. package/src/Filter/FilterBar/_docs/FilterBar.stickersheet.stories.tsx +1 -1
  69. package/src/Filter/FilterBar/_docs/FilterBar.stories.tsx +1 -1
  70. package/src/Filter/FilterBar/context/FilterBarContext.tsx +17 -5
  71. package/src/Filter/FilterBar/context/reducer/filterBarStateReducer.spec.ts +3 -0
  72. package/src/Filter/FilterBar/context/reducer/filterBarStateReducer.ts +1 -1
  73. package/src/Filter/FilterBar/context/reducer/setupFilterBarState.spec.tsx +40 -0
  74. package/src/Filter/FilterBar/context/reducer/setupFilterBarState.ts +5 -0
  75. package/src/Filter/FilterBar/context/reducer/updateSingleFilter.spec.ts +2 -0
  76. package/src/Filter/FilterBar/context/reducer/updateValues.spec.ts +5 -0
  77. package/src/Filter/FilterBar/context/types.ts +1 -0
  78. package/src/Filter/FilterBar/context/utils/checkShouldUpdateValues.spec.ts +1 -0
  79. package/src/Filter/FilterBar/context/utils/getInactiveFilters.spec.ts +2 -0
  80. package/src/Filter/FilterBar/context/utils/getIsUsableWhenArgs.spec.ts +1 -0
  81. package/src/Filter/FilterBar/context/utils/updateDependentFilters.spec.ts +8 -0
  82. package/src/Filter/FilterBar/context/utils/updateDependentFilters.ts +1 -1
  83. package/src/Filter/FilterBar/subcomponents/ClearAllButton/ClearAllButton.module.scss +4 -0
  84. package/src/Filter/FilterBar/subcomponents/ClearAllButton/ClearAllButton.tsx +5 -2
  85. package/src/Filter/FilterDateRangePicker/subcomponents/FilterDateRangePickerField/FilterDateRangePickerField.tsx +4 -0
  86. package/src/Tile/TileGrid/_docs/TileGrid.stories.tsx +41 -8
  87. package/src/Tile/subcomponents/GenericTile/GenericTile.spec.stories.tsx +58 -0
  88. package/src/Tile/subcomponents/GenericTile/GenericTile.tsx +24 -1
@@ -38,6 +38,7 @@ describe("updateDependentFilters()", () => {
38
38
  values: { flavour: "jasmine" },
39
39
  dependentFilterIds: new Set(),
40
40
  hasUpdatedValues: false,
41
+ hasRemovableFilter: false,
41
42
  } satisfies FilterBarState<Values>
42
43
 
43
44
  const newState = updateDependentFilters<Values>(state)
@@ -53,6 +54,7 @@ describe("updateDependentFilters()", () => {
53
54
  values: { flavour: "jasmine" },
54
55
  dependentFilterIds: new Set<keyof Values>(["sugarLevel"]),
55
56
  hasUpdatedValues: false,
57
+ hasRemovableFilter: false,
56
58
  } satisfies FilterBarState<Values>
57
59
 
58
60
  const newState = updateDependentFilters<Values>(state)
@@ -77,6 +79,7 @@ describe("updateDependentFilters()", () => {
77
79
  values: { flavour: "jasmine" },
78
80
  dependentFilterIds: new Set<keyof Values>(["sugarLevel"]),
79
81
  hasUpdatedValues: false,
82
+ hasRemovableFilter: false,
80
83
  } satisfies FilterBarState<Values>
81
84
 
82
85
  updateDependentFilters<Values>(state)
@@ -98,6 +101,7 @@ describe("updateDependentFilters()", () => {
98
101
  values: { flavour: "jasmine" },
99
102
  dependentFilterIds: new Set<keyof Values>(["sugarLevel"]),
100
103
  hasUpdatedValues: false,
104
+ hasRemovableFilter: false,
101
105
  } satisfies FilterBarState<Values>
102
106
 
103
107
  const newState = updateDependentFilters<Values>(state)
@@ -117,6 +121,7 @@ describe("updateDependentFilters()", () => {
117
121
  values: { flavour: "jasmine" },
118
122
  dependentFilterIds: new Set<keyof Values>(["sugarLevel"]),
119
123
  hasUpdatedValues: false,
124
+ hasRemovableFilter: false,
120
125
  } satisfies FilterBarState<Values>
121
126
 
122
127
  const newState = updateDependentFilters<Values>(state)
@@ -139,6 +144,7 @@ describe("updateDependentFilters()", () => {
139
144
  values: { flavour: "jasmine", sugarLevel: 50 },
140
145
  dependentFilterIds: new Set<keyof Values>(["sugarLevel"]),
141
146
  hasUpdatedValues: false,
147
+ hasRemovableFilter: false,
142
148
  } satisfies FilterBarState<Values>
143
149
 
144
150
  const newState = updateDependentFilters<Values>(state)
@@ -161,6 +167,7 @@ describe("updateDependentFilters()", () => {
161
167
  values: { flavour: "jasmine" },
162
168
  dependentFilterIds: new Set<keyof Values>(["sugarLevel"]),
163
169
  hasUpdatedValues: false,
170
+ hasRemovableFilter: false,
164
171
  } satisfies FilterBarState<Values>
165
172
 
166
173
  const newState = updateDependentFilters<Values>(state)
@@ -176,6 +183,7 @@ describe("updateDependentFilters()", () => {
176
183
  values: { sugarLevel: 50 },
177
184
  dependentFilterIds: new Set<keyof Values>(["sugarLevel"]),
178
185
  hasUpdatedValues: false,
186
+ hasRemovableFilter: false,
179
187
  } satisfies FilterBarState<Values>
180
188
 
181
189
  const newState = updateDependentFilters<Values>(state)
@@ -22,7 +22,7 @@ export const updateDependentFilters = <ValuesMap extends FiltersValues>(
22
22
 
23
23
  if (!isUsable) {
24
24
  state.activeFilterIds.delete(id)
25
- state.values[id] = undefined
25
+ delete state.values[id]
26
26
  state.hasUpdatedValues = true
27
27
  return
28
28
  }
@@ -1,3 +1,7 @@
1
1
  .clearAllButton {
2
2
  white-space: nowrap;
3
3
  }
4
+
5
+ .hidden {
6
+ visibility: hidden;
7
+ }
@@ -1,5 +1,6 @@
1
1
  import React from "react"
2
2
  import { useIntl } from "@cultureamp/i18n-react-intl"
3
+ import classnames from "classnames"
3
4
  import { Button } from "~components/__actions__/v2"
4
5
  import { useFilterBarContext } from "../../context/FilterBarContext"
5
6
  import styles from "./ClearAllButton.module.scss"
@@ -19,13 +20,15 @@ export const ClearAllButton = (): JSX.Element => {
19
20
  description: "Button aria-label to clear all values within the filter bar",
20
21
  })
21
22
 
22
- const { clearAllFilters } = useFilterBarContext()
23
+ const { clearAllFilters, isClearable } = useFilterBarContext()
23
24
 
24
25
  return (
25
26
  <Button
26
27
  label={clearButtonLabel}
27
28
  aria-label={clearButtonAriaLabel}
28
- classNameOverride={styles.clearAllButton}
29
+ classNameOverride={classnames(styles.clearAllButton, {
30
+ [styles.hidden]: !isClearable,
31
+ })}
29
32
  secondary
30
33
  onClick={clearAllFilters}
31
34
  />
@@ -259,6 +259,10 @@ export const FilterDateRangePickerField = ({
259
259
  return
260
260
  }
261
261
 
262
+ if (state.inputStartValue === "" && state.inputEndValue === "") {
263
+ return
264
+ }
265
+
262
266
  const newStartDate = validateStartDate(
263
267
  selectedRange?.from,
264
268
  state.inputStartValue
@@ -1,5 +1,6 @@
1
1
  import React from "react"
2
2
  import { Meta, StoryObj } from "@storybook/react"
3
+ import { expect, waitFor, within } from "@storybook/test"
3
4
  import { InformationTile } from "~components/Tile"
4
5
  import { TileGrid } from "../index"
5
6
 
@@ -10,21 +11,21 @@ const meta = {
10
11
  children: (
11
12
  <>
12
13
  <InformationTile
13
- title="Title"
14
+ title="Title A"
14
15
  metadata="Side A"
15
- information="Side B"
16
+ information="Side A - Back"
16
17
  footer={<>Footer</>}
17
18
  />
18
19
  <InformationTile
19
- title="Title"
20
- metadata="Side A"
21
- information="Side B"
20
+ title="Title B"
21
+ metadata="Side B"
22
+ information="Side B - Back"
22
23
  footer={<>Footer</>}
23
24
  />
24
25
  <InformationTile
25
- title="Title"
26
- metadata="Side A"
27
- information="Side B"
26
+ title="Title C"
27
+ metadata="Side C"
28
+ information="Side C - Back"
28
29
  footer={<>Footer</>}
29
30
  />
30
31
  </>
@@ -45,3 +46,35 @@ export const Playground: Story = {
45
46
  },
46
47
  },
47
48
  }
49
+
50
+ // Test for multiple tiles, flipping one doesn't flip others
51
+ export const FlipOneNotOthers: Story = {
52
+ play: async ({ canvasElement, step }) => {
53
+ const canvas = within(canvasElement)
54
+
55
+ await step("initial render complete", async () => {
56
+ await waitFor(() => {
57
+ canvas.getByRole("button", {
58
+ name: "View more information: Title A",
59
+ })
60
+ })
61
+ })
62
+
63
+ await step("Can focus to button", async () => {
64
+ await waitFor(() => {
65
+ const buttonWithInfoLabel = canvas.getByRole("button", {
66
+ name: "View more information: Title A",
67
+ })
68
+ buttonWithInfoLabel.click()
69
+ })
70
+ })
71
+
72
+ await step("Check other tiles", async () => {
73
+ await waitFor(() => {
74
+ expect(canvas.getByText("Side A - Back")).toBeInTheDocument()
75
+ expect(canvas.getByText("Title B")).toBeInTheDocument()
76
+ expect(canvas.getByText("Title C")).toBeInTheDocument()
77
+ })
78
+ })
79
+ },
80
+ }
@@ -87,3 +87,61 @@ export const InfoButtonLabel: Story = {
87
87
  })
88
88
  },
89
89
  }
90
+
91
+ export const DoesNotStealFocusOnInitialRender: Story = {
92
+ play: async ({ canvasElement, step }) => {
93
+ const canvas = within(canvasElement)
94
+
95
+ await step("initial render complete", async () => {
96
+ await waitFor(() => {
97
+ canvas.getByRole("button", {
98
+ name: "View more information: Title",
99
+ })
100
+ })
101
+ })
102
+
103
+ await step("Can focus to button", async () => {
104
+ await waitFor(() => {
105
+ const buttonWithInfoLabel = canvas.getByRole("button", {
106
+ name: "View more information: Title",
107
+ })
108
+ expect(buttonWithInfoLabel).not.toHaveFocus()
109
+ })
110
+ })
111
+ },
112
+ }
113
+
114
+ export const FocusOnFlip: Story = {
115
+ play: async ({ canvasElement, step }) => {
116
+ const canvas = within(canvasElement)
117
+ const buttonWithInfoLabel = await canvas.findByRole("button", {
118
+ name: "View more information: Title",
119
+ })
120
+
121
+ await step("initial render complete", async () => {
122
+ expect(buttonWithInfoLabel).toBeInTheDocument()
123
+ })
124
+
125
+ await step("Can focus to button", async () => {
126
+ await waitFor(() => {
127
+ buttonWithInfoLabel.click()
128
+ })
129
+ })
130
+
131
+ const returnButton = canvas.getByRole("button", {
132
+ name: "Hide information: Title",
133
+ })
134
+
135
+ await step("Can click on info button again", async () => {
136
+ await waitFor(() => {
137
+ returnButton.click()
138
+ })
139
+ })
140
+
141
+ await step("Info button has focus again", async () => {
142
+ await waitFor(() => {
143
+ expect(buttonWithInfoLabel).toHaveFocus()
144
+ })
145
+ })
146
+ },
147
+ }
@@ -1,4 +1,4 @@
1
- import React, { HTMLAttributes, useState } from "react"
1
+ import React, { HTMLAttributes, useState, useRef, useEffect } from "react"
2
2
  import { useIntl } from "@cultureamp/i18n-react-intl"
3
3
  import classnames from "classnames"
4
4
  import { AllowedHeadingTags, Heading } from "~components/Heading"
@@ -57,7 +57,28 @@ export const GenericTile = ({
57
57
  ...restProps
58
58
  }: GenericTileProps): JSX.Element => {
59
59
  const [isFlipped, setIsFlipped] = useState<boolean>(false)
60
+ const [isDocumentReady, setIsDocumentReady] = useState<boolean>(false)
61
+
60
62
  const { formatMessage } = useIntl()
63
+ const infoButtonRef = useRef<HTMLButtonElement>(null)
64
+ const infoButtonReturnRef = useRef<HTMLButtonElement>(null)
65
+
66
+ useEffect(() => {
67
+ setIsDocumentReady(true)
68
+ }, [])
69
+
70
+ useEffect(() => {
71
+ if (!isDocumentReady) {
72
+ setIsDocumentReady(true)
73
+ return
74
+ }
75
+
76
+ if (isFlipped) {
77
+ infoButtonReturnRef.current!.focus()
78
+ } else {
79
+ infoButtonRef.current!.focus()
80
+ }
81
+ }, [isFlipped])
61
82
 
62
83
  const translatedInfoLabel = formatMessage({
63
84
  id: "kzGenericTile.infoButtonLabel",
@@ -97,6 +118,7 @@ export const GenericTile = ({
97
118
  onClick={(): void => setIsFlipped(true)}
98
119
  disabled={isFlipped}
99
120
  aria-hidden={isFlipped}
121
+ ref={infoButtonRef}
100
122
  />
101
123
  </div>
102
124
  )}
@@ -162,6 +184,7 @@ export const GenericTile = ({
162
184
  onClick={(): void => setIsFlipped(false)}
163
185
  disabled={!isFlipped}
164
186
  aria-hidden={!isFlipped}
187
+ ref={infoButtonReturnRef}
165
188
  />
166
189
  </div>
167
190
  <div className={styles.information}>