@usequeek/theme-kit 0.1.7 → 0.1.8
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/README.md +74 -0
- package/client.ts +1 -0
- package/components/powered-by-queek.tsx +36 -0
- package/package.json +1 -1
- package/tests/powered-by-queek.test.tsx +37 -0
package/README.md
CHANGED
|
@@ -54,6 +54,80 @@ Theme CSS ships with the package: `@usequeek/theme-kit/shared-blocks/core-blocks
|
|
|
54
54
|
source. Anyone testing a theme with vitest needs the same `inline` line;
|
|
55
55
|
anyone compiling one needs `moduleResolution: bundler` + `jsx: react-jsx`.
|
|
56
56
|
|
|
57
|
+
## Custom data: metafields & metaobjects
|
|
58
|
+
|
|
59
|
+
Product pages receive custom data already resolved — never fetch it:
|
|
60
|
+
|
|
61
|
+
```tsx
|
|
62
|
+
import { ProductMetafields } from '@usequeek/theme-kit/components/product-metafields';
|
|
63
|
+
import type { ProductPageProps } from '@usequeek/theme-kit/types/theme';
|
|
64
|
+
|
|
65
|
+
export default function ProductPage({ product, metafieldDefinitions }: ProductPageProps) {
|
|
66
|
+
return <ProductMetafields product={product} definitions={metafieldDefinitions} />;
|
|
67
|
+
}
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
- MUST: place `<ProductMetafields product definitions={metafieldDefinitions} />`
|
|
71
|
+
on the product page. `product.metafields` (`types/product.ts`) arrives as
|
|
72
|
+
presented values — a scalar, scalar list, `MetaobjectEntry`, or
|
|
73
|
+
`MetaobjectEntry[]`; labels come via `metafieldDefinitions` (unknown keys
|
|
74
|
+
fall back to a humanised key). The component renders nothing when there is
|
|
75
|
+
nothing visible to show.
|
|
76
|
+
- MAY: compose your own layout with the exported `EntryCard` and `renderValue`
|
|
77
|
+
from the same module instead of placing the section.
|
|
78
|
+
- Style only: the section emits `core-metafields*` classes (`core-metafields`,
|
|
79
|
+
`__field`, `__label`, `__value`, `__text`, `__link`, `__image`, `__pre`,
|
|
80
|
+
`__list`, `__list-item`, `__entry-card`, `__entry-title`, `__entry-field`,
|
|
81
|
+
`__entry-field-label`, `__entry-field-value`, `__entry-grid`);
|
|
82
|
+
`DefaultMetaobjectPage` emits `core-metaobject-page*`. No kit CSS ships for
|
|
83
|
+
these — style them in your theme.
|
|
84
|
+
- The `metaobjects` section block (`shared-blocks/metaobjects`,
|
|
85
|
+
framework-owned) renders a merchant-picked type's entries when the page
|
|
86
|
+
supplies its data; it emits `core-block-metaobjects` with `--grid`/`--list`
|
|
87
|
+
modifiers, `core-block-metaobjects__list` with `--grid`/`--list` variants,
|
|
88
|
+
plus `__title` and `__card-link`. Never re-implement it — style the classes.
|
|
89
|
+
- Optional entry pages: provide `pages.Metaobject: FC<MetaobjectPageProps>`
|
|
90
|
+
(`entry` plus `definition { type; name; fields: [{ key; name; type }] }`).
|
|
91
|
+
When absent, core `DefaultMetaobjectPage`
|
|
92
|
+
(`components/default-metaobject-page`) is used.
|
|
93
|
+
- Dynamic sources (`$source` refs) resolve server-side; block data always
|
|
94
|
+
reaches the theme as literals — nothing to handle.
|
|
95
|
+
|
|
96
|
+
## Attribution: Powered by Queek
|
|
97
|
+
|
|
98
|
+
Every storefront is a billboard — every footer variant of every theme renders
|
|
99
|
+
the core-owned attribution link:
|
|
100
|
+
|
|
101
|
+
```tsx
|
|
102
|
+
import { PoweredByQueek } from '@usequeek/theme-kit/components/powered-by-queek';
|
|
103
|
+
|
|
104
|
+
export default function Footer() {
|
|
105
|
+
return (
|
|
106
|
+
<footer>
|
|
107
|
+
{/* ... */}
|
|
108
|
+
<PoweredByQueek />
|
|
109
|
+
</footer>
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
- MUST: place `<PoweredByQueek />` on every footer variant (`footers/*.tsx`,
|
|
115
|
+
or `footer.tsx` where a theme has no `footers/` dir). The placement and
|
|
116
|
+
spacing are the theme's — the component is the content, the footer owns
|
|
117
|
+
where it sits. `theme-check` rule `theme/footer-shows-powered-by` enforces
|
|
118
|
+
this, so a theme cannot ship without it.
|
|
119
|
+
- Style only: the link emits `core-powered-by` with a
|
|
120
|
+
`core-powered-by__brand` hook on the `Queek` wordmark; an optional
|
|
121
|
+
`className` passthrough lets a footer position it (e.g.
|
|
122
|
+
`<PoweredByQueek className="my-footer__powered" />`). No kit CSS ships for
|
|
123
|
+
it — style the classes in your theme.
|
|
124
|
+
- The href (`https://usequeek.com/business`), `target="_blank"` and
|
|
125
|
+
`rel="noopener noreferrer"` are the contract — never re-implement the
|
|
126
|
+
markup by hand.
|
|
127
|
+
- `hidden` (default false) takes the component's visibility from a prop as
|
|
128
|
+
the seam for a possible future plan perk. There is no merchant-facing
|
|
129
|
+
toggle for it in v1 — always render it visible.
|
|
130
|
+
|
|
57
131
|
## Boundary
|
|
58
132
|
|
|
59
133
|
The kit must never import the storefront app, its themes, or `lib/storefront`.
|
package/client.ts
CHANGED
|
@@ -13,6 +13,7 @@ export * from './shared-blocks';
|
|
|
13
13
|
export * from './components/blog';
|
|
14
14
|
export * from './components/image';
|
|
15
15
|
export * from './components/product-metafields';
|
|
16
|
+
export * from './components/powered-by-queek';
|
|
16
17
|
export * from './components/default-metaobject-page';
|
|
17
18
|
export * from './components/product-media-frame';
|
|
18
19
|
export * from './components/variant-picker';
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { JSX } from 'react';
|
|
2
|
+
|
|
3
|
+
export interface PoweredByQueekProps {
|
|
4
|
+
/** Extra class so a footer can position the link — styling stays theme-owned. */
|
|
5
|
+
className?: string;
|
|
6
|
+
/**
|
|
7
|
+
* Reserved for a future plan perk (paid removal). Defaults to false and is
|
|
8
|
+
* always rendered in v1 — there is no merchant-facing toggle for it.
|
|
9
|
+
*/
|
|
10
|
+
hidden?: boolean;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Core-owned storefront attribution — the `ProductMetafields` pattern: themes
|
|
15
|
+
* import-and-place it, never re-implement it.
|
|
16
|
+
*
|
|
17
|
+
* Required on every footer variant of every theme (enforced by `theme-check`
|
|
18
|
+
* rule `theme/footer-shows-powered-by` in the storefront repo). The link
|
|
19
|
+
* target (`https://usequeek.com/business`) and `rel` are the contract —
|
|
20
|
+
* do not change them. Markup carries `core-powered-by` classes so each theme
|
|
21
|
+
* styles it in its own `theme.css`; no kit CSS ships for it.
|
|
22
|
+
*/
|
|
23
|
+
export function PoweredByQueek({ className, hidden = false }: PoweredByQueekProps): JSX.Element | null {
|
|
24
|
+
if (hidden) return null;
|
|
25
|
+
|
|
26
|
+
return (
|
|
27
|
+
<a
|
|
28
|
+
href="https://usequeek.com/business"
|
|
29
|
+
target="_blank"
|
|
30
|
+
rel="noopener noreferrer"
|
|
31
|
+
className={className ? `core-powered-by ${className}` : 'core-powered-by'}
|
|
32
|
+
>
|
|
33
|
+
Powered by <strong className="core-powered-by__brand">Queek</strong>
|
|
34
|
+
</a>
|
|
35
|
+
);
|
|
36
|
+
}
|
package/package.json
CHANGED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { renderToStaticMarkup } from 'react-dom/server';
|
|
3
|
+
import { describe, expect, it } from 'vitest';
|
|
4
|
+
import { PoweredByQueek } from '../components/powered-by-queek';
|
|
5
|
+
|
|
6
|
+
function render(props?: { className?: string; hidden?: boolean }): string {
|
|
7
|
+
return renderToStaticMarkup(React.createElement(PoweredByQueek, props));
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
describe('PoweredByQueek', () => {
|
|
11
|
+
it('links to the Queek business page with the attribution contract intact', () => {
|
|
12
|
+
const html = render();
|
|
13
|
+
|
|
14
|
+
expect(html).toContain('<a');
|
|
15
|
+
expect(html).toContain('href="https://usequeek.com/business"');
|
|
16
|
+
expect(html).toContain('target="_blank"');
|
|
17
|
+
expect(html).toContain('rel="noopener noreferrer"');
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it('renders "Powered by Queek" with the brand hook for theme styling', () => {
|
|
21
|
+
const html = render();
|
|
22
|
+
|
|
23
|
+
expect(html).toContain('core-powered-by');
|
|
24
|
+
expect(html).toContain('Powered by');
|
|
25
|
+
expect(html).toContain('<strong class="core-powered-by__brand">Queek</strong>');
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it('passes a footer positioning class through alongside the core class', () => {
|
|
29
|
+
const html = render({ className: 'qn-footer4__powered' });
|
|
30
|
+
|
|
31
|
+
expect(html).toContain('class="core-powered-by qn-footer4__powered"');
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it('renders nothing when hidden', () => {
|
|
35
|
+
expect(render({ hidden: true })).toBe('');
|
|
36
|
+
});
|
|
37
|
+
});
|