diff --git a/packages/@headlessui-tailwindcss/CHANGELOG.md b/packages/@headlessui-tailwindcss/CHANGELOG.md index eb03729..a682f72 100644 --- a/packages/@headlessui-tailwindcss/CHANGELOG.md +++ b/packages/@headlessui-tailwindcss/CHANGELOG.md @@ -7,7 +7,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] -- Nothing yet! +### Fixed + +- Fix bracket order of `not` variants ([#1621](https://github.com/tailwindlabs/headlessui/pull/1621)) ## [0.1.0] - 2022-05-24 diff --git a/packages/@headlessui-tailwindcss/jest.config.cjs b/packages/@headlessui-tailwindcss/jest.config.cjs new file mode 100644 index 0000000..f841b1d --- /dev/null +++ b/packages/@headlessui-tailwindcss/jest.config.cjs @@ -0,0 +1,5 @@ +let create = require('../../jest/create-jest-config.cjs') +module.exports = create(__dirname, { + displayName: ' CSS ', + setupFilesAfterEnv: ['./jest.setup.js'], +}) diff --git a/packages/@headlessui-tailwindcss/jest.setup.js b/packages/@headlessui-tailwindcss/jest.setup.js new file mode 100644 index 0000000..04649d6 --- /dev/null +++ b/packages/@headlessui-tailwindcss/jest.setup.js @@ -0,0 +1,54 @@ +let prettier = require('prettier') + +function format(input) { + return prettier.format(input.replace(/\n/g, ''), { + parser: 'css', + printWidth: 100, + }) +} + +expect.extend({ + // Compare two CSS strings with all whitespace removed + // This is probably naive but it's fast and works well enough. + toMatchFormattedCss(received = '', argument = '') { + let options = { + comment: 'stripped(received) === stripped(argument)', + isNot: this.isNot, + promise: this.promise, + } + + let formattedReceived = format(received) + let formattedArgument = format(argument) + + let pass = formattedReceived === formattedArgument + + let message = pass + ? () => { + return ( + this.utils.matcherHint('toMatchFormattedCss', undefined, undefined, options) + + '\n\n' + + `Expected: not ${this.utils.printExpected(formattedReceived)}\n` + + `Received: ${this.utils.printReceived(formattedArgument)}` + ) + } + : () => { + let actual = formattedReceived + let expected = formattedArgument + + let diffString = this.utils.diff(expected, actual, { + expand: this.expand, + }) + + return ( + this.utils.matcherHint('toMatchFormattedCss', undefined, undefined, options) + + '\n\n' + + (diffString && diffString.includes('- Expect') + ? `Difference:\n\n${diffString}` + : `Expected: ${this.utils.printExpected(expected)}\n` + + `Received: ${this.utils.printReceived(actual)}`) + ) + } + + return { actual: received, message, pass } + }, +}) diff --git a/packages/@headlessui-tailwindcss/src/index.test.ts b/packages/@headlessui-tailwindcss/src/index.test.ts new file mode 100644 index 0000000..e896ae3 --- /dev/null +++ b/packages/@headlessui-tailwindcss/src/index.test.ts @@ -0,0 +1,93 @@ +import path from 'path' +import postcss from 'postcss' +import tailwind from 'tailwindcss' +import hui from './index' + +let html = String.raw +let css = String.raw + +function run(input: string, config: any, plugin = tailwind) { + let { currentTestName } = expect.getState() + + return postcss(plugin(config)).process(input, { + from: `${path.resolve(__filename)}?test=${currentTestName}`, + }) +} + +it('should generate css for an exposed state', async () => { + let config = { + content: [{ raw: html`
` }], + plugins: [hui], + } + + return run('@tailwind utilities', config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + .ui-open\:underline[data-headlessui-state~='open'] { + text-decoration-line: underline; + } + :where([data-headlessui-state~='open']) .ui-open\:underline { + text-decoration-line: underline; + } + `) + }) +}) + +it('should generate the inverse "not" css for an exposed state', async () => { + let config = { + content: [{ raw: html`` }], + plugins: [hui], + } + + return run('@tailwind utilities', config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + .ui-not-open\:underline[data-headlessui-state]:not([data-headlessui-state~='open']) { + text-decoration-line: underline; + } + + :where([data-headlessui-state]:not([data-headlessui-state~='open'])) + .ui-not-open\:underline:not([data-headlessui-state]) { + text-decoration-line: underline; + } + `) + }) +}) + +describe('custom prefix', () => { + it('should generate css for an exposed state', async () => { + let config = { + content: [{ raw: html`` }], + plugins: [hui({ prefix: 'hui' })], + } + + return run('@tailwind utilities', config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + .hui-open\:underline[data-headlessui-state~='open'] { + text-decoration-line: underline; + } + :where([data-headlessui-state~='open']) .hui-open\:underline { + text-decoration-line: underline; + } + `) + }) + }) + + it('should generate the inverse "not" css for an exposed state', async () => { + let config = { + content: [{ raw: html`` }], + plugins: [hui({ prefix: 'hui' })], + } + + return run('@tailwind utilities', config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + .hui-not-open\:underline[data-headlessui-state]:not([data-headlessui-state~='open']) { + text-decoration-line: underline; + } + + :where([data-headlessui-state]:not([data-headlessui-state~='open'])) + .hui-not-open\:underline:not([data-headlessui-state]) { + text-decoration-line: underline; + } + `) + }) + }) +}) diff --git a/packages/@headlessui-tailwindcss/src/index.ts b/packages/@headlessui-tailwindcss/src/index.ts index 50400e6..b825eea 100644 --- a/packages/@headlessui-tailwindcss/src/index.ts +++ b/packages/@headlessui-tailwindcss/src/index.ts @@ -29,7 +29,7 @@ export default plugin.withOptions