Skip to content

Commit

Permalink
feature: ActionBlock with InlineLink (#226)
Browse files Browse the repository at this point in the history
# Changes

- Adds an Action Block model & component that can be used to add one or
more prestyled actions (only Inline Link for now) to pages or other
content blocks.
- Fixes the `npm create:...` (Plop) files so that test files are
correctly added when creating a new Block or Component. (Not technically
part of this PR, but I needed it to create the new blocks anyway).

```
┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄
┆ ╔═════════════╗  ┌─────────────┐            ┆
┆ ║   Primary   ║  │  Secondary  │   Default  ┆  = action group
┆ ╚═════════════╝  └─────────────┘   ───────  ┆
┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄
```

# Associated issue

Resolves #218

# How to test

1. Open preview link
2. Verify the home page has an ActionBlock above the fold and that it
works
3. Navigate to the ActionBlock Demo page
(`/en/demos/action-block-demo/`)
4. Verify that all the variations work

# Checklist

- [x] I have performed a self-review of my own code
- [x] I have made sure that my PR is easy to review (not too big,
includes comments)
- [x] I have made updated relevant documentation files (in project
README, docs/, etc)
- ~I have added a decision log entry if the change affects the
architecture or changes a significant technology~
- [x] I have notified a reviewer

<!-- Please strike through and check off all items that do not apply
(rather than removing them) -->
  • Loading branch information
jbmoelker authored Dec 16, 2024
1 parent f6a2bb4 commit d1065bc
Show file tree
Hide file tree
Showing 18 changed files with 456 additions and 13 deletions.
263 changes: 263 additions & 0 deletions config/datocms/migrations/1734363388_actionBlock.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,263 @@
import { Client } from '@datocms/cli/lib/cma-client-node';

export default async function (client: Client) {
console.log('Create new models/block models');

console.log(
'Create block model "\uD83C\uDF9B\uFE0F Action Block" (`action_block`)'
);
await client.itemTypes.create(
{
id: 'F60ZY1wFSW2qbvh99poj3g',
name: '\uD83C\uDF9B\uFE0F Action Block',
api_key: 'action_block',
modular_block: true,
inverse_relationships_enabled: false,
},
{
skip_menu_item_creation: true,
schema_menu_item_id: 'aV5wDaZcRLaLFiNqBpNstw',
}
);

console.log(
'Create block model "\uD83D\uDD17 Internal Link" (`internal_link`)'
);
await client.itemTypes.create(
{
id: 'GWnhoQDqQoGJj4-sQTVttw',
name: '\uD83D\uDD17 Internal Link',
api_key: 'internal_link',
modular_block: true,
inverse_relationships_enabled: false,
},
{
skip_menu_item_creation: true,
schema_menu_item_id: 'aVOBPryiQ8O5EiHEHDEang',
}
);

console.log('Creating new fields/fieldsets');

console.log(
'Create Modular Content (Multiple blocks) field "Items" (`items`) in block model "\uD83C\uDF9B\uFE0F Action Block" (`action_block`)'
);
await client.fields.create('F60ZY1wFSW2qbvh99poj3g', {
id: 'dAUckF8qR0edf_f7zam6hA',
label: 'Items',
field_type: 'rich_text',
api_key: 'items',
validators: {
rich_text_blocks: { item_types: ['GWnhoQDqQoGJj4-sQTVttw'] },
size: { min: 1 },
},
appearance: {
addons: [],
editor: 'rich_text',
parameters: { start_collapsed: true },
},
default_value: null,
});

console.log(
'Create Single-line string field "Title" (`title`) in block model "\uD83D\uDD17 Internal Link" (`internal_link`)'
);
await client.fields.create('GWnhoQDqQoGJj4-sQTVttw', {
id: 'XTl0xPRsTpWg9zFauwDl5Q',
label: 'Title',
field_type: 'string',
api_key: 'title',
validators: { required: {} },
appearance: {
addons: [],
editor: 'single_line',
parameters: { heading: false, placeholder: null },
},
default_value: '',
});

console.log(
'Create Single link field "Link" (`link`) in block model "\uD83D\uDD17 Internal Link" (`internal_link`)'
);
await client.fields.create('GWnhoQDqQoGJj4-sQTVttw', {
id: 'bN5K_JObRQqv7tkzt4RG2w',
label: 'Link',
field_type: 'link',
api_key: 'link',
validators: {
item_item_type: {
on_publish_with_unpublished_references_strategy: 'fail',
on_reference_unpublish_strategy: 'delete_references',
on_reference_delete_strategy: 'delete_references',
item_types: ['LjXdkuCdQxCFT4hv8_ayew', 'X_tZn3TxQY28ltSyjZUGHQ'],
},
required: {},
},
appearance: { addons: [], editor: 'link_select', parameters: {} },
default_value: null,
});

console.log(
'Create Single-line string field "Style" (`style`) in block model "\uD83D\uDD17 Internal Link" (`internal_link`)'
);
await client.fields.create('GWnhoQDqQoGJj4-sQTVttw', {
id: 'S3JQgijhRmalePX3GeugPg',
label: 'Style',
field_type: 'string',
api_key: 'style',
validators: {
required: {},
enum: { values: ['default', 'primary', 'secondary'] },
},
appearance: {
addons: [],
editor: 'string_select',
parameters: {
options: [
{ hint: '', label: 'Default action', value: 'default' },
{ hint: '', label: 'Primary action', value: 'primary' },
{ hint: '', label: 'Secondary action', value: 'secondary' },
],
},
},
default_value: 'default',
});

console.log('Update existing fields/fieldsets');

console.log(
'Update Structured text field "Text" (`text`) in block model "\uD83D\uDCDD \uD83D\uDDBC\uFE0F Text Image Block" (`text_image_block`)'
);
await client.fields.update('V4dMfrWsQ027JYEp6q3KhA', {
validators: {
required: {},
structured_text_blocks: {
item_types: [
'F60ZY1wFSW2qbvh99poj3g',
'ZdBokLsWRgKKjHrKeJzdpw',
'gezG9nO7SfaiWcWnp-HNqw',
'0SxYNS2CR1it_5LHYWuEQg',
],
},
structured_text_links: {
on_publish_with_unpublished_references_strategy: 'fail',
on_reference_unpublish_strategy: 'delete_references',
on_reference_delete_strategy: 'delete_references',
item_types: [
'GjWw8t-hTFaYYWyc53FeIg',
'LjXdkuCdQxCFT4hv8_ayew',
'X_tZn3TxQY28ltSyjZUGHQ',
],
},
},
});

console.log(
'Update Modular Content (Multiple blocks) field "Body" (`blocks`) in model "\uD83D\uDCCE Page Partial" (`page_partial`)'
);
await client.fields.update('SKLmdv71Rge0rKhJzOFQWQ', {
validators: {
rich_text_blocks: {
item_types: [
'BRbU6VwTRgmG5SbwUs0rBg',
'F60ZY1wFSW2qbvh99poj3g',
'PAk40zGjQJCcDXXPgygUrA',
'ZdBokLsWRgKKjHrKeJzdpw',
'gezG9nO7SfaiWcWnp-HNqw',
'0SxYNS2CR1it_5LHYWuEQg',
],
},
},
});

console.log(
'Update Modular Content (Multiple blocks) field "Body" (`body_blocks`) in model "\uD83D\uDCD1 Page" (`page`)'
);
await client.fields.update('Q-z1nyMsQtC8Sr6w6J2oGw', {
validators: {
rich_text_blocks: {
item_types: [
'BRbU6VwTRgmG5SbwUs0rBg',
'F60ZY1wFSW2qbvh99poj3g',
'PAk40zGjQJCcDXXPgygUrA',
'QYfZyBzIRWKxA1MinIR0aQ',
'VZvVfu52RZK81WG0Dxp-FQ',
'V80liDVtRC-UYgd3Sm-dXg',
'ZdBokLsWRgKKjHrKeJzdpw',
'gezG9nO7SfaiWcWnp-HNqw',
'0SxYNS2CR1it_5LHYWuEQg',
],
},
},
});

console.log(
'Update Structured text field "Text" (`text`) in block model "\uD83D\uDCDD Text Block" (`text_block`)'
);
await client.fields.update('NtVXfZ6gTL2sKNffNeUf5Q', {
validators: {
required: {},
structured_text_blocks: {
item_types: [
'F60ZY1wFSW2qbvh99poj3g',
'QYfZyBzIRWKxA1MinIR0aQ',
'ZdBokLsWRgKKjHrKeJzdpw',
'gezG9nO7SfaiWcWnp-HNqw',
'0SxYNS2CR1it_5LHYWuEQg',
],
},
structured_text_links: {
on_publish_with_unpublished_references_strategy: 'fail',
on_reference_unpublish_strategy: 'delete_references',
on_reference_delete_strategy: 'delete_references',
item_types: [
'GjWw8t-hTFaYYWyc53FeIg',
'LjXdkuCdQxCFT4hv8_ayew',
'X_tZn3TxQY28ltSyjZUGHQ',
],
},
},
});

console.log(
'Update Modular Content (Multiple blocks) field "Body" (`body_blocks`) in model "\uD83C\uDFE0 Home" (`home_page`)'
);
await client.fields.update('pUj2PObgTyC-8X4lvZLMBA', {
validators: {
rich_text_blocks: {
item_types: [
'BRbU6VwTRgmG5SbwUs0rBg',
'F60ZY1wFSW2qbvh99poj3g',
'PAk40zGjQJCcDXXPgygUrA',
'QYfZyBzIRWKxA1MinIR0aQ',
'VZvVfu52RZK81WG0Dxp-FQ',
'V80liDVtRC-UYgd3Sm-dXg',
'ZdBokLsWRgKKjHrKeJzdpw',
'gezG9nO7SfaiWcWnp-HNqw',
'0SxYNS2CR1it_5LHYWuEQg',
],
},
},
});

console.log(
'Update Modular Content (Multiple blocks) field "Body" (`body_blocks`) in model "\uD83E\uDD37 Not found" (`not_found_page`)'
);
await client.fields.update('Zu006Xq0TMCAvV-vyQ_Iiw', {
validators: {
rich_text_blocks: {
item_types: [
'BRbU6VwTRgmG5SbwUs0rBg',
'F60ZY1wFSW2qbvh99poj3g',
'PAk40zGjQJCcDXXPgygUrA',
'QYfZyBzIRWKxA1MinIR0aQ',
'VZvVfu52RZK81WG0Dxp-FQ',
'V80liDVtRC-UYgd3Sm-dXg',
'ZdBokLsWRgKKjHrKeJzdpw',
'gezG9nO7SfaiWcWnp-HNqw',
'0SxYNS2CR1it_5LHYWuEQg',
],
},
},
});
}
24 changes: 24 additions & 0 deletions config/plop/plopfile.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,12 @@ export default function (plop) {
name: 'name',
message: 'Block name (e.g. TextBlock)?',
},
{
type: 'confirm',
name: 'test',
message: 'Add test file (.test.ts)?',
default: false,
},
{
type: 'confirm',
name: 'readme',
Expand All @@ -62,6 +68,12 @@ export default function (plop) {
path: '../../src/blocks/{{ pascalCase name }}/{{ pascalCase name }}.fragment.graphql',
templateFile: 'templates/block/Block.fragment.graphql.hbs',
},
{
type: 'add',
path: '../../src/blocks/{{ pascalCase name }}/{{ pascalCase name }}.test.ts',
templateFile: 'templates/block/Block.test.ts.hbs',
skip: (data) => !data.test && 'No test file',
},
{
type: 'add',
path: '../../src/blocks/{{ pascalCase name }}/README.md',
Expand Down Expand Up @@ -91,6 +103,12 @@ export default function (plop) {
message: 'Add custom element?',
default: false,
},
{
type: 'confirm',
name: 'test',
message: 'Add test file (.test.ts)?',
default: false,
},
{
type: 'confirm',
name: 'readme',
Expand All @@ -110,6 +128,12 @@ export default function (plop) {
templateFile: 'templates/component/Component.client.ts.hbs',
skip: (data) => !data.script && 'No client-side script',
},
{
type: 'add',
path: '../../src/components/{{ pascalCase name }}/{{ pascalCase name }}.test.ts',
templateFile: 'templates/component/Component.client.ts.hbs',
skip: (data) => !data.test && 'No test file',
},
{
type: 'add',
path: '../../src/components/{{ pascalCase name }}/README.md',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ import {{ pascalCase name }}, { type Props } from './{{ pascalCase name }}.astro
describe('{{ pascalCase name }}', () => {
test('Block is rendered', async () => {
const fragment = await renderToFragment<Props>({{ pascalCase name }}, {
props: {
block: {}
}
props: {
block: {}
}
});

expect(fragment).toBeTruthy();
Expand Down
2 changes: 1 addition & 1 deletion datocms-environment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,5 @@
* @see docs/getting-started.md on how to use this file
* @see docs/decision-log/2023-10-24-datocms-env-file.md on why file is preferred over env vars
*/
export const datocmsEnvironment = '148-file-downloads';
export const datocmsEnvironment = 'action-block';
export const datocmsBuildTriggerId = '30535';
45 changes: 45 additions & 0 deletions src/blocks/ActionBlock/ActionBlock.astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
---
import type { ActionBlockFragment } from '@lib/datocms/types';
import LinkToRecord from '@components/LinkToRecord/LinkToRecord.astro';
export interface Props {
block: ActionBlockFragment;
}
const { block } = Astro.props;
const { items } = block;
---

<div class="action-block">
{
items.map((item) => (
<LinkToRecord
record={item.link}
class:list={['action', `action--${item.style}`]}
>
{item.title}
</LinkToRecord>
))
}
</div>

<style>
/* basic styling, can be replaced */
.action-block {
display: flex;
gap: 1em;
}

.action {
padding: 10px;
}

.action--primary {
background: black;
color: white;
}

.action--secondary {
border: 2px solid currentColor;
color: currentColor;
}
</style>
12 changes: 12 additions & 0 deletions src/blocks/ActionBlock/ActionBlock.fragment.graphql
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#import './InternalLink.fragment.graphql'

fragment ActionBlock on ActionBlockRecord {
__typename
id
items {
__typename
... on InternalLinkRecord {
...InternalLink
}
}
}
Loading

0 comments on commit d1065bc

Please sign in to comment.