@recursica/mui-adapter 0.34.0 → 0.34.1

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.
package/package.json CHANGED
@@ -13,7 +13,7 @@
13
13
  "url": "git+https://github.com/borderux/recursica.git",
14
14
  "directory": "packages/mui-adapter"
15
15
  },
16
- "version": "0.34.0",
16
+ "version": "0.34.1",
17
17
  "publishConfig": {
18
18
  "access": "public"
19
19
  },
@@ -59,7 +59,9 @@
59
59
  "lint": "eslint .",
60
60
  "storybook": "storybook dev -p 6012",
61
61
  "analyze-tokens": "analyze-tokens --css @recursica/official-release/recursica_variables_scoped.css --dir src/components --output token-analysis.json",
62
- "prebuild": "npm run analyze-tokens"
62
+ "prebuild": "npm run analyze-tokens",
63
+ "adapter-tester": "adapter-tester --serve",
64
+ "adapter-tester:automated": "adapter-tester"
63
65
  },
64
66
  "devDependencies": {
65
67
  "@chromatic-com/storybook": "^5.1.1",
@@ -67,6 +69,8 @@
67
69
  "@mui/lab": "^7.0.1-beta.25",
68
70
  "@mui/x-date-pickers": "^9.11.0",
69
71
  "@mui/x-tree-view": "^9.11.0",
72
+ "@playwright/test": "^1.53.0",
73
+ "@recursica/adapter-tester": "*",
70
74
  "@recursica/recursica-postcss-vars": "*",
71
75
  "@recursica/storybook-template": "*",
72
76
  "@recursica/token-analyzer": "*",
@@ -120,6 +120,31 @@ export const ErrorState: Story = {
120
120
  },
121
121
  };
122
122
 
123
+ export const OpenedCalendar: Story = {
124
+ args: {
125
+ label: "Meeting Date",
126
+ assistiveText: "Calendar rendered open by default for styling review.",
127
+ // MUI X's DatePicker supports a controlled `open` prop directly; pairing it with a
128
+ // no-op `onClose` keeps the calendar open with no click interaction needed — same
129
+ // intent as the mantine-adapter's `OpenedCalendar` story.
130
+ open: true,
131
+ onClose: () => {},
132
+ // Fixed (not computed) so the selected-day fill is visible on load, alongside the
133
+ // today marker, for styling review. Local-component constructor, not an ISO date
134
+ // string — `new Date("2026-08-26")` parses as UTC midnight, which renders as the
135
+ // 25th in any timezone behind UTC.
136
+ defaultValue: new Date(2026, 7, 26),
137
+ },
138
+ parameters: {
139
+ docs: {
140
+ description: {
141
+ story:
142
+ "The calendar dropdown renders open by default so its styling can be reviewed without a click interaction.",
143
+ },
144
+ },
145
+ },
146
+ };
147
+
123
148
  export const StaticReadOnly: Story = {
124
149
  args: {
125
150
  label: "Static ReadOnly Review",
@@ -1,3 +1,14 @@
1
+ /* Brand-layer exemptions (recursica-allow-brand) — see recursica-token-analyzer README.md.
2
+ * Close button is styled to match the Button component's text/icon-only variant, so it reuses
3
+ * the same global hover/focus state tokens Button.module.css exempts.
4
+ * recursica-allow-brand: --recursica_brand_states_focus_blur
5
+ * recursica-allow-brand: --recursica_brand_states_focus_border-size
6
+ * recursica-allow-brand: --recursica_brand_states_focus_color
7
+ * recursica-allow-brand: --recursica_brand_states_focus_margin
8
+ * recursica-allow-brand: --recursica_brand_states_hover_color
9
+ * recursica-allow-brand: --recursica_brand_states_hover_opacity
10
+ */
11
+
1
12
  .root {
2
13
  }
3
14
 
@@ -40,6 +51,9 @@
40
51
  }
41
52
 
42
53
  .header {
54
+ display: flex; /* HARDCODE: puts the title and close button side-by-side so the title has a bounded width to truncate against */
55
+ align-items: center;
56
+ justify-content: space-between;
43
57
  padding: var(
44
58
  --recursica_ui-kit_components_modal_properties_header-footer-vertical-padding
45
59
  )
@@ -50,6 +64,12 @@
50
64
  }
51
65
 
52
66
  .title {
67
+ flex: 1 1 auto; /* HARDCODE: let the title claim the space between the header edge and the close button */
68
+ min-width: 0; /* HARDCODE: required for text-overflow ellipsis to take effect on a flex child */
69
+ overflow: hidden;
70
+ white-space: nowrap;
71
+ text-overflow: ellipsis;
72
+
53
73
  color: var(--recursica_ui-kit_components_modal_properties_colors_title);
54
74
 
55
75
  /* Direct Figma Typography Mapping */
@@ -173,5 +193,87 @@
173
193
  }
174
194
 
175
195
  .close {
176
- /* Inherits standard icon button tokens natively, but we can override if required */
196
+ /* Matches the Button component's text-variant, icon-only, small-size visual treatment
197
+ (see Button.module.css) so the modal close control looks like a Recursica Button
198
+ rather than MUI's native IconButton. */
199
+ box-sizing: border-box;
200
+ display: flex;
201
+ align-items: center;
202
+ justify-content: center;
203
+ position: relative;
204
+ overflow: hidden;
205
+ transition: all 0.2s ease;
206
+
207
+ height: var(
208
+ --recursica_ui-kit_components_button_variants_sizes_small_properties_height
209
+ );
210
+ min-width: var(
211
+ --recursica_ui-kit_components_button_variants_content_icon-only_variants_sizes_small_properties_min-width
212
+ );
213
+ padding: 0
214
+ var(
215
+ --recursica_ui-kit_components_button_variants_content_icon-only_variants_sizes_small_properties_horizontal-padding
216
+ );
217
+ border-radius: var(
218
+ --recursica_ui-kit_components_button_variants_content_icon-only_variants_sizes_small_properties_border-radius
219
+ );
220
+
221
+ border-style: solid;
222
+ border-width: var(
223
+ --recursica_ui-kit_components_button_variants_styles_text_properties_border-size
224
+ );
225
+ border-color: var(
226
+ --recursica_ui-kit_components_button_variants_styles_text_properties_colors_border-color
227
+ );
228
+ background-color: var(
229
+ --recursica_ui-kit_components_button_variants_styles_text_properties_colors_background-color
230
+ );
231
+ color: var(
232
+ --recursica_ui-kit_components_button_variants_styles_text_properties_colors_icon-color
233
+ );
234
+ }
235
+
236
+ .close svg {
237
+ position: relative;
238
+ z-index: 1;
239
+ width: var(
240
+ --recursica_ui-kit_components_button_variants_sizes_small_properties_icon
241
+ );
242
+ height: var(
243
+ --recursica_ui-kit_components_button_variants_sizes_small_properties_icon
244
+ );
245
+ }
246
+
247
+ .close::after {
248
+ content: "";
249
+ position: absolute;
250
+ inset: 0;
251
+ border-radius: inherit;
252
+ z-index: 0;
253
+ pointer-events: none;
254
+ transition: opacity 150ms ease;
255
+ opacity: 0;
256
+ background-color: var(--recursica_brand_states_hover_color);
257
+ }
258
+ .close:hover:not(:disabled)::after {
259
+ opacity: var(--recursica_brand_states_hover_opacity);
260
+ }
261
+
262
+ /* MuiIconButton injects its own `:hover` background via emotion at render time, which lands in
263
+ the DOM after this stylesheet and wins the tie at equal specificity — same class of conflict
264
+ documented in DatePicker.module.css's error/disabled state overrides. `!important` keeps the
265
+ button's own background transparent so only the `::after` overlay above renders the hover
266
+ feedback, matching Button's single-overlay hover treatment exactly. */
267
+ .close:hover {
268
+ background-color: transparent !important;
269
+ }
270
+
271
+ .close:focus-visible {
272
+ outline: none;
273
+ box-shadow:
274
+ 0 0 0 var(--recursica_brand_states_focus_border-size)
275
+ var(--recursica_brand_states_focus_color),
276
+ 0 0 var(--recursica_brand_states_focus_blur)
277
+ var(--recursica_brand_states_focus_margin)
278
+ var(--recursica_brand_states_focus_color);
177
279
  }
@@ -19,7 +19,8 @@ export default meta;
19
19
  type Story = StoryObj<typeof Modal>;
20
20
 
21
21
  const DefaultWrapper = (args: ModalProps) => {
22
- const [opened, setOpened] = useState(false);
22
+ // Starts opened so the modal is visible without pressing a button first.
23
+ const [opened, setOpened] = useState(true);
23
24
  return (
24
25
  <>
25
26
  <Modal {...args} opened={opened} onClose={() => setOpened(false)}>
@@ -43,8 +44,33 @@ export const Default: Story = {
43
44
  render: (args) => <DefaultWrapper {...args} />,
44
45
  };
45
46
 
47
+ const LongTitleWrapper = (args: ModalProps) => {
48
+ const [opened, setOpened] = useState(true);
49
+ return (
50
+ <>
51
+ <Modal {...args} opened={opened} onClose={() => setOpened(false)}>
52
+ The title above is longer than the header can display, so it truncates
53
+ with an ellipsis instead of wrapping onto a second line.
54
+ <Modal.Footer>
55
+ <Button onClick={() => setOpened(false)}>Got it</Button>
56
+ </Modal.Footer>
57
+ </Modal>
58
+ <Button onClick={() => setOpened(true)}>Open Modal</Button>
59
+ </>
60
+ );
61
+ };
62
+
63
+ export const LongTitle: Story = {
64
+ args: {
65
+ title:
66
+ "This Modal Title Is Deliberately Long Enough To Exceed The Available Header Width",
67
+ },
68
+ render: (args) => <LongTitleWrapper {...args} />,
69
+ };
70
+
46
71
  const ScrollingWrapper = (args: ModalProps) => {
47
- const [opened, setOpened] = useState(false);
72
+ // Starts opened so the modal is visible without pressing a button first.
73
+ const [opened, setOpened] = useState(true);
48
74
  return (
49
75
  <>
50
76
  <Modal {...args} opened={opened} onClose={() => setOpened(false)}>