Skip to content

Commit c424086

Browse files
Add support for named groups and peers in Tailwind plugin (#5178)
* Fix Tailwind plugin group variant regression * lint * add support for named groups to tailwind plugin * add to example app * cleanup * fix example * add support for named peers --------- Co-authored-by: Devon Govett <devongovett@gmail.com>
1 parent 5ce3502 commit c424086

File tree

3 files changed

+55
-10
lines changed

3 files changed

+55
-10
lines changed

examples/rac-spectrum-tailwind/src/App.js

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { useState } from "react";
22
import { defaultTheme, Provider } from "@adobe/react-spectrum";
3-
import { Label, Radio, RadioGroup } from "react-aria-components";
3+
import { Label, Radio, RadioGroup, Button } from "react-aria-components";
44
import User from "@spectrum-icons/workflow/User";
55
import UserGroup from "@spectrum-icons/workflow/UserGroup";
66
import Building from "@spectrum-icons/workflow/Building";
@@ -23,6 +23,15 @@ export function App() {
2323
<p>For the purpose of ensuring Tailwind's default selectors still work for non-RAC elements when using the plugin.</p>
2424
</div>
2525
</div>
26+
<div className="flex justify-center">
27+
<div className="flex flex-col">
28+
<Button className="flex flex-col justify-center bg-white dark:bg-black border rounded group/button p-160 m-160 focus:outline-none focus-visible:ring">
29+
<div className="w-full">Test Button</div>
30+
<div className="w-full invisible group-hover/button:visible">Group is hovered</div>
31+
</Button>
32+
<p>For the purpose of testing named groups.</p>
33+
</div>
34+
</div>
2635
</div>
2736
</Provider>
2837
);

packages/tailwindcss-react-aria-components/src/index.js

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -78,26 +78,42 @@ let mapSelector = (selector, fn) => {
7878
}
7979
};
8080

81-
module.exports = plugin.withOptions((options) => (({addVariant}) => {
81+
let addVariants = (variantName, selectors, addVariant, matchVariant) => {
82+
addVariant(variantName, selectors);
83+
matchVariant(
84+
'group',
85+
(_, {modifier}) =>
86+
modifier
87+
? mapSelector(selectors, selector => `:merge(.group\\/${modifier})${selector.slice(1)} &`)
88+
: mapSelector(selectors, selector => `:merge(.group)${selector.slice(1)} &`),
89+
{values: {[variantName]: variantName}}
90+
);
91+
matchVariant(
92+
'peer',
93+
(_, {modifier}) =>
94+
modifier
95+
? mapSelector(selectors, selector => `:merge(.peer\\/${modifier})${selector.slice(1)} ~ &`)
96+
: mapSelector(selectors, selector => `:merge(.peer)${selector.slice(1)} ~ &`),
97+
{values: {[variantName]: variantName}}
98+
);
99+
};
100+
101+
module.exports = plugin.withOptions((options) => (({addVariant, matchVariant}) => {
82102
let prefix = options?.prefix ? `${options.prefix}-` : '';
83103
attributes.boolean.forEach((attribute) => {
84104
let variantName = Array.isArray(attribute) ? attribute[0] : attribute;
85105
variantName = `${prefix}${variantName}`;
86106
let attributeName = Array.isArray(attribute) ? attribute[1] : attribute;
87107
let selectors = getSelector(prefix, attributeName);
88-
addVariant(variantName, selectors);
89-
addVariant(`group-${variantName}`, mapSelector(selectors, selector => `:merge(.group)${selector.slice(1)} &`));
90-
addVariant(`peer-${variantName}`, mapSelector(selectors, selector => `:merge(.peer)${selector.slice(1)} ~ &`));
108+
addVariants(variantName, selectors, addVariant, matchVariant);
91109
});
92110
Object.keys(attributes.enum).forEach((attributeName) => {
93111
attributes.enum[attributeName].forEach(
94112
(attributeValue) => {
95113
let name = shortNames[attributeName] || attributeName;
96114
let variantName = `${prefix}${name}-${attributeValue}`;
97115
let selectors = getSelector(prefix, attributeName, attributeValue);
98-
addVariant(variantName, selectors);
99-
addVariant(`group-${variantName}`, mapSelector(selectors, selector => `:merge(.group)${selector.slice(1)} &`));
100-
addVariant(`peer-${variantName}`, mapSelector(selectors, selector => `:merge(.peer)${selector.slice(1)} ~ &`));
116+
addVariants(variantName, selectors, addVariant, matchVariant);
101117
}
102118
);
103119
});

packages/tailwindcss-react-aria-components/src/index.test.js

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ function run({options, content}) {
2323
}
2424

2525
test('variants', async () => {
26-
let content = html`<div data-rac className="hover:bg-red focus:bg-red focus-visible:bg-red focus-within:bg-red pressed:bg-red disabled:bg-red drop-target:bg-red dragging:bg-red empty:bg-red allows-dragging:bg-red allows-removing:bg-red allows-sorting:bg-red placeholder-shown:bg-red selected:bg-red indeterminate:bg-red read-only:bg-red required:bg-red entering:bg-red exiting:bg-red open:bg-red unavailable:bg-red outside-month:bg-red outside-visible-range:bg-red selection-start:bg-red selection-end:bg-red current:bg-red invalid:bg-red resizing:bg-red placement-left:bg-red placement-right:bg-red placement-top:bg-red placement-bottom:bg-red type-literal:bg-red type-year:bg-red type-month:bg-red type-day:bg-red layout-grid:bg-red layout-stack:bg-red orientation-horizontal:bg-red orientation-vertical:bg-red selection-single:bg-red selection-multiple:bg-red resizable-right:bg-red resizable-left:bg-red resizable-both:bg-red sort-ascending:bg-red sort-descending:bg-red group-pressed:bg-red peer-pressed:bg-red group-hover:bg-red"></div>`;
26+
let content = html`<div data-rac className="hover:bg-red focus:bg-red focus-visible:bg-red focus-within:bg-red pressed:bg-red disabled:bg-red drop-target:bg-red dragging:bg-red empty:bg-red allows-dragging:bg-red allows-removing:bg-red allows-sorting:bg-red placeholder-shown:bg-red selected:bg-red indeterminate:bg-red read-only:bg-red required:bg-red entering:bg-red exiting:bg-red open:bg-red unavailable:bg-red outside-month:bg-red outside-visible-range:bg-red selection-start:bg-red selection-end:bg-red current:bg-red invalid:bg-red resizing:bg-red placement-left:bg-red placement-right:bg-red placement-top:bg-red placement-bottom:bg-red type-literal:bg-red type-year:bg-red type-month:bg-red type-day:bg-red layout-grid:bg-red layout-stack:bg-red orientation-horizontal:bg-red orientation-vertical:bg-red selection-single:bg-red selection-multiple:bg-red resizable-right:bg-red resizable-left:bg-red resizable-both:bg-red sort-ascending:bg-red sort-descending:bg-red group-pressed:bg-red peer-pressed:bg-red group-hover:bg-red group/custom-name group-hover/custom-name:bg-red peer-pressed/custom-name:bg-red"></div>`;
2727
return run({content}).then((result) => {
2828
expect(result.css).toContain(css`
2929
.hover\:bg-red:where([data-rac])[data-hovered] {
@@ -34,10 +34,18 @@ test('variants', async () => {
3434
--tw-bg-opacity: 1;
3535
background-color: rgb(255 0 0 / var(--tw-bg-opacity))
3636
}
37+
.group\/custom-name:where([data-rac])[data-hovered] .group-hover\/custom-name\:bg-red {
38+
--tw-bg-opacity: 1;
39+
background-color: rgb(255 0 0 / var(--tw-bg-opacity))
40+
}
3741
.group:where([data-rac])[data-hovered] .group-hover\:bg-red {
3842
--tw-bg-opacity: 1;
3943
background-color: rgb(255 0 0 / var(--tw-bg-opacity))
4044
}
45+
.group\/custom-name:where(:not([data-rac])):hover .group-hover\/custom-name\:bg-red {
46+
--tw-bg-opacity: 1;
47+
background-color: rgb(255 0 0 / var(--tw-bg-opacity))
48+
}
4149
.group:where(:not([data-rac])):hover .group-hover\:bg-red {
4250
--tw-bg-opacity: 1;
4351
background-color: rgb(255 0 0 / var(--tw-bg-opacity))
@@ -74,6 +82,10 @@ test('variants', async () => {
7482
--tw-bg-opacity: 1;
7583
background-color: rgb(255 0 0 / var(--tw-bg-opacity))
7684
}
85+
.peer\/custom-name[data-pressed] ~ .peer-pressed\/custom-name\:bg-red {
86+
--tw-bg-opacity: 1;
87+
background-color: rgb(255 0 0 / var(--tw-bg-opacity))
88+
}
7789
.peer[data-pressed] ~ .peer-pressed\:bg-red {
7890
--tw-bg-opacity: 1;
7991
background-color: rgb(255 0 0 / var(--tw-bg-opacity))
@@ -283,13 +295,17 @@ test('variants', async () => {
283295
});
284296

285297
test('variants with prefix', async () => {
286-
let content = html`<div data-rac className="rac-hover:bg-red rac-focus:bg-red rac-focus-visible:bg-red rac-focus-within:bg-red rac-pressed:bg-red rac-disabled:bg-red rac-drop-target:bg-red rac-dragging:bg-red rac-empty:bg-red rac-allows-dragging:bg-red rac-allows-removing:bg-red rac-allows-sorting:bg-red rac-placeholder-shown:bg-red rac-selected:bg-red rac-indeterminate:bg-red rac-read-only:bg-red rac-required:bg-red rac-entering:bg-red rac-exiting:bg-red rac-open:bg-red rac-unavailable:bg-red rac-outside-month:bg-red rac-outside-visible-range:bg-red rac-selection-start:bg-red rac-selection-end:bg-red rac-current:bg-red rac-invalid:bg-red rac-resizing:bg-red rac-placement-left:bg-red rac-placement-right:bg-red rac-placement-top:bg-red rac-placement-bottom:bg-red rac-type-literal:bg-red rac-type-year:bg-red rac-type-month:bg-red rac-type-day:bg-red rac-layout-grid:bg-red rac-layout-stack:bg-red rac-orientation-horizontal:bg-red rac-orientation-vertical:bg-red rac-selection-single:bg-red rac-selection-multiple:bg-red rac-resizable-right:bg-red rac-resizable-left:bg-red rac-resizable-both:bg-red rac-sort-ascending:bg-red rac-sort-descending:bg-red group-rac-pressed:bg-red peer-rac-pressed:bg-red"></div>`;
298+
let content = html`<div data-rac className="rac-hover:bg-red rac-focus:bg-red rac-focus-visible:bg-red rac-focus-within:bg-red rac-pressed:bg-red rac-disabled:bg-red rac-drop-target:bg-red rac-dragging:bg-red rac-empty:bg-red rac-allows-dragging:bg-red rac-allows-removing:bg-red rac-allows-sorting:bg-red rac-placeholder-shown:bg-red rac-selected:bg-red rac-indeterminate:bg-red rac-read-only:bg-red rac-required:bg-red rac-entering:bg-red rac-exiting:bg-red rac-open:bg-red rac-unavailable:bg-red rac-outside-month:bg-red rac-outside-visible-range:bg-red rac-selection-start:bg-red rac-selection-end:bg-red rac-current:bg-red rac-invalid:bg-red rac-resizing:bg-red rac-placement-left:bg-red rac-placement-right:bg-red rac-placement-top:bg-red rac-placement-bottom:bg-red rac-type-literal:bg-red rac-type-year:bg-red rac-type-month:bg-red rac-type-day:bg-red rac-layout-grid:bg-red rac-layout-stack:bg-red rac-orientation-horizontal:bg-red rac-orientation-vertical:bg-red rac-selection-single:bg-red rac-selection-multiple:bg-red rac-resizable-right:bg-red rac-resizable-left:bg-red rac-resizable-both:bg-red rac-sort-ascending:bg-red rac-sort-descending:bg-red group-rac-pressed:bg-red group/custom-name group-rac-hover/custom-name:bg-red peer-rac-pressed:bg-red peer-rac-pressed/custom-name:bg-red"></div>`;
287299
return run({content, options: {prefix: 'rac'}}).then((result) => {
288300
expect(result.css).toContain(css`
289301
.rac-hover\:bg-red[data-hovered] {
290302
--tw-bg-opacity: 1;
291303
background-color: rgb(255 0 0 / var(--tw-bg-opacity))
292304
}
305+
.group\/custom-name[data-hovered] .group-rac-hover\/custom-name\:bg-red {
306+
--tw-bg-opacity: 1;
307+
background-color: rgb(255 0 0 / var(--tw-bg-opacity))
308+
}
293309
.rac-focus\:bg-red[data-focused] {
294310
--tw-bg-opacity: 1;
295311
background-color: rgb(255 0 0 / var(--tw-bg-opacity))
@@ -310,6 +326,10 @@ test('variants with prefix', async () => {
310326
--tw-bg-opacity: 1;
311327
background-color: rgb(255 0 0 / var(--tw-bg-opacity))
312328
}
329+
.peer\/custom-name[data-pressed] ~ .peer-rac-pressed\/custom-name\:bg-red {
330+
--tw-bg-opacity: 1;
331+
background-color: rgb(255 0 0 / var(--tw-bg-opacity))
332+
}
313333
.peer[data-pressed] ~ .peer-rac-pressed\:bg-red {
314334
--tw-bg-opacity: 1;
315335
background-color: rgb(255 0 0 / var(--tw-bg-opacity))

0 commit comments

Comments
 (0)