Skip to content

Commit 20b3333

Browse files
committed
Merge branch 'main' into dynamic-fonts
2 parents a3397b2 + 05c1c9f commit 20b3333

File tree

11 files changed

+164
-36
lines changed

11 files changed

+164
-36
lines changed

.changeset/eleven-bulldogs-hug.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@qwik-ui/headless': patch
3+
---
4+
5+
refactor: strip browser user agent styles relating to max-width

apps/website/src/components/mdx-components/index.tsx

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,13 @@ export const components: Record<string, Component> = {
2525
</p>
2626
);
2727
}),
28+
a: component$<PropsOf<'a'>>(({ ...props }) => {
29+
return (
30+
<a {...props} class={[cn('text-primary', props.class)]}>
31+
<Slot />
32+
</a>
33+
);
34+
}),
2835
h1: component$<PropsOf<'h1'>>(({ ...props }) => {
2936
return (
3037
<h1

apps/website/src/routes/docs/headless/collapsible/examples/open-change.tsx

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,22 +6,28 @@ import SVG from './svg';
66
export default component$(() => {
77
useStyles$(styles);
88
const count = useSignal<number>(0);
9+
const isOpen = useSignal<boolean>(false);
910

1011
const handleOpenChange$ = $((open: boolean) => {
11-
if (open) {
12-
count.value++;
13-
}
12+
isOpen.value = open;
13+
count.value++;
1414
});
1515

1616
return (
17-
<Collapsible class="collapsible" onOpenChange$={handleOpenChange$}>
18-
<CollapsibleTrigger class="collapsible-trigger">
19-
<span>Trigger</span>
20-
<SVG />
21-
</CollapsibleTrigger>
22-
<CollapsibleContent class="collapsible-content collapsible-content-outline ">
23-
Content: {count.value}
24-
</CollapsibleContent>
25-
</Collapsible>
17+
<>
18+
<p>
19+
count: <strong> {count.value}</strong>
20+
</p>
21+
22+
<Collapsible class="collapsible" onOpenChange$={handleOpenChange$}>
23+
<CollapsibleTrigger class="collapsible-trigger">
24+
<span>Trigger</span>
25+
<SVG />
26+
</CollapsibleTrigger>
27+
<CollapsibleContent class="collapsible-content collapsible-content-outline ">
28+
Content
29+
</CollapsibleContent>
30+
</Collapsible>
31+
</>
2632
);
2733
});

apps/website/src/routes/docs/headless/collapsible/examples/programmatic.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import styles from '../snippets/collapsible.css?inline';
44

55
export default component$(() => {
66
useStyles$(styles);
7-
const isOpen = useSignal<boolean>(true);
7+
const isOpen = useSignal<boolean>(false);
88

99
return (
1010
<>

apps/website/src/routes/docs/headless/modal/index.mdx

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ Custom signal binds are like a remote control for components, controlling states
104104

105105
## The Top Layer
106106

107-
<image src={testImage} class="rounded-base mb-6" />
107+
<image src={testImage} class="mb-6 rounded-base" />
108108

109109
In Chrome and Edge, a `top layer` UI button is shown in the dev tools. This means the element content is placed above any other content on the page, preventing any overflow or style issues.
110110

@@ -154,6 +154,20 @@ This means that if a user is navigating through the modal using the Tab key, rea
154154

155155
> Focus locking behavior is provided by default in Qwik UI. If you encounter any use cases that require customized focus lock behavior, please [submit an issue](https://github.com/qwikifiers/qwik-ui/issues) on our GitHub repository.
156156
157+
## Stripped Styles
158+
159+
As a headless library, we intentionally try not to add any styles to the components.
160+
161+
However, because Qwik UI builds on top of native solutions when they are well-supported, feasible, and performant, some of the widgets may inclue browser user-agent styles.
162+
163+
These styles can be unintuitive tricky to debug. Which has been the case with Qwik UI's own docs site. As a result, we have stripped these styles from the Modal component.
164+
165+
<CodeSnippet name="stripped-styles.css" />
166+
167+
While in most cases, this would be up to a consumer's CSS reset to solve, in this case we are **stripping** the max-width and max-height styles on the dialog element under the hood.
168+
169+
This is done in a separate layer so that styles are easily overridable in consumer facing applications.
170+
157171
## Animations
158172

159173
Animating things to display none has historically been a significant challenge on the web. This is because display none is a `discrete` property, and is **unanimatable**.
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
@layer qwik-ui {
2+
/* browsers automatically set an interesting max-width and max-height for dialogs
3+
https://twitter.com/t3dotgg/status/1774350919133691936
4+
*/
5+
dialog:modal {
6+
max-width: unset;
7+
max-height: unset;
8+
}
9+
}

apps/website/src/routes/docs/styled/(getting-started)/install/index.mdx

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,25 @@ title: 'Qwik UI | Styled Kit - Installation'
99
Simply run the following command:
1010

1111
```zsh
12-
npx qwik-ui init
12+
pnpm dlx qwik-ui init
13+
```
14+
15+
(or "**npx qwik-ui init**" if you prefer npm)
16+
17+
This will help you install tailwind, create a `qwik-ui.config.json` file and modify your root/global css and tailwind config files with the necessary Qwik UI design system variables and values ([see below](#manual-installation))
18+
19+
### Generating components using the CLI
20+
21+
When you want to add a component you can run:
22+
23+
```zsh
24+
pnpm qwik-ui add
25+
```
26+
27+
Or if you know a specific component you want to add, let's say "`input`", you can run:
28+
29+
```zsh
30+
pnpm qwik-ui add input
1331
```
1432

1533
## Manual installation

apps/website/src/routes/[email protected]

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -40,15 +40,13 @@ export default component$(() => {
4040
return (
4141
<div class="flex flex-col items-center gap-8 py-24">
4242
<h1 class="text-center text-4xl leading-normal lg:text-5xl">
43-
<span class="text-outlined font-extrabold tracking-wide text-primary">Qwik</span>{' '}
43+
<span class="font-extrabold tracking-wide text-primary">Qwik</span>{' '}
4444
<span class="font-extrabold tracking-wide text-secondary">UI</span>
4545
</h1>
4646
<h2 class="text-center text-xl font-bold leading-normal lg:text-3xl">
4747
Headless & styled copy-paste components
4848
<br />
49-
<span class="text-outlined leading-normal text-primary">
50-
automatically optimized for you
51-
</span>
49+
<span class="leading-normal text-primary">automatically optimized for you</span>
5250
</h2>
5351
<p class="text-center text-lg lg:text-xl">
5452
Choose a kit and start building the future{' '}

cla-signs/v1/cla.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -423,6 +423,14 @@
423423
"created_at": "2024-03-24T20:05:54Z",
424424
"repoId": 545159943,
425425
"pullRequestNo": 665
426+
},
427+
{
428+
"name": "siguici",
429+
"id": 62618356,
430+
"comment_id": 2035079637,
431+
"created_at": "2024-04-03T16:37:30Z",
432+
"repoId": 545159943,
433+
"pullRequestNo": 675
426434
}
427435
]
428436
}

packages/kit-headless/src/components/collapsible/collapsible.test.ts

Lines changed: 72 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ async function setup(page: Page, exampleName: string) {
1212
}
1313

1414
test.describe('Mouse Behavior', () => {
15-
test(`GIVEN a hero collapsible
15+
test(`GIVEN a collapsible
1616
WHEN clicking on the trigger
1717
THEN the content should be visible
1818
AND aria-expanded is true`, async ({ page }) => {
@@ -24,13 +24,11 @@ test.describe('Mouse Behavior', () => {
2424
await expect(d.getTrigger()).toHaveAttribute('aria-expanded', 'true');
2525
});
2626

27-
test(`GIVEN an open hero collapsible
27+
test(`GIVEN an open collapsible
2828
WHEN clicking on the trigger
2929
THEN the content should be hidden
3030
AND aria-expanded is false`, async ({ page }) => {
31-
const { driver: d } = await setup(page, 'hero');
32-
await d.openCollapsible('click');
33-
31+
const { driver: d } = await setup(page, 'open');
3432
await d.getTrigger().click();
3533

3634
await expect(d.getContent()).toBeHidden();
@@ -55,9 +53,7 @@ test.describe('Keyboard Behavior', () => {
5553
WHEN pressing the space key
5654
THEN the content should be hidden
5755
AND aria-expanded is false`, async ({ page }) => {
58-
const { driver: d } = await setup(page, 'hero');
59-
await d.openCollapsible('Space');
60-
56+
const { driver: d } = await setup(page, 'open');
6157
await d.getTrigger().press('Space');
6258

6359
await expect(d.getContent()).toBeHidden();
@@ -80,9 +76,7 @@ test.describe('Keyboard Behavior', () => {
8076
WHEN pressing the enter key
8177
THEN the content should be hidden
8278
AND aria-expanded is false`, async ({ page }) => {
83-
const { driver: d } = await setup(page, 'hero');
84-
await d.openCollapsible('Enter');
85-
79+
const { driver: d } = await setup(page, 'open');
8680
await d.getTrigger().press('Enter');
8781

8882
await expect(d.getContent()).toBeHidden();
@@ -111,7 +105,6 @@ test.describe('Animations', () => {
111105
THEN the content should open`, async ({ page }) => {
112106
const { driver: d } = await setup(page, 'animation');
113107

114-
await d.getTrigger().focus();
115108
await d.getTrigger().click();
116109
await d.waitForAnimationEnd('[data-collapsible-content]');
117110
await expect(d.getContent()).toBeVisible();
@@ -123,11 +116,76 @@ test.describe('Animations', () => {
123116
const { driver: d } = await setup(page, 'animation');
124117
await d.openCollapsible('click');
125118

126-
await d.getTrigger().focus();
127119
await d.getTrigger().click();
128120
await d.waitForAnimationEnd('[data-collapsible-content]');
129121
await expect(d.getContent()).toBeHidden();
130122
});
131123
});
132124

133-
test.describe('Content resizing', () => {});
125+
test.describe('Reactive values', () => {
126+
test(`GIVEN a collapsible with a bind:open prop
127+
WHEN the signal value changes to true
128+
THEN the content should be visible
129+
`, async ({ page }) => {
130+
const { driver: d } = await setup(page, 'programmatic');
131+
132+
// our example uses bind:checked on the checkbox with our same signal.
133+
134+
await d.locator.getByRole('checkbox').check();
135+
await expect(d.getContent()).toBeVisible();
136+
});
137+
138+
test(`GIVEN a collapsible with a bind:open prop
139+
WHEN the signal value changes to false
140+
THEN the content should be hidden
141+
`, async ({ page }) => {
142+
const { driver: d } = await setup(page, 'programmatic');
143+
144+
await d.locator.getByRole('checkbox').uncheck();
145+
await expect(d.getContent()).toBeHidden();
146+
});
147+
});
148+
149+
test.describe('Handlers', () => {
150+
test(`GIVEN a collapsible with an onOpenChange$ prop
151+
WHEN the content is opened
152+
THEN the handler should be called
153+
`, async ({ page }) => {
154+
const { driver: d } = await setup(page, 'open-change');
155+
156+
const countText = d.locator.getByRole('paragraph');
157+
await expect(countText).toHaveText('count: 0');
158+
await d.openCollapsible('click');
159+
160+
await expect(countText).toHaveText('count: 1');
161+
});
162+
163+
test(`GIVEN a collapsible with an onOpenChange$ prop
164+
WHEN the content is closed
165+
THEN the handler should be called
166+
`, async ({ page }) => {
167+
const { driver: d } = await setup(page, 'open-change');
168+
169+
const countText = d.locator.getByRole('paragraph');
170+
await d.openCollapsible('click');
171+
await expect(countText).toHaveText('count: 1');
172+
await d.getTrigger().click();
173+
174+
await expect(countText).toHaveText('count: 2');
175+
});
176+
});
177+
178+
test.describe('Disabled', () => {
179+
test(`GIVEN a collapsible with a disabled prop
180+
WHEN the trigger is clicked
181+
THEN the content should remain closed
182+
`, async ({ page }) => {
183+
const { driver: d } = await setup(page, 'disabled');
184+
185+
await expect(d.getTrigger()).toBeDisabled();
186+
187+
// actionability checks are only for enabled elements
188+
await d.getTrigger().click({ force: true });
189+
await expect(d.getContent()).toBeHidden();
190+
});
191+
});

0 commit comments

Comments
 (0)