replace button link block with a separate button link tool inside draftail

This commit is contained in:
2026-07-05 00:51:30 +02:00
parent b0698247b6
commit 6f1d531d5d
25 changed files with 620 additions and 219 deletions
@@ -0,0 +1,24 @@
# Generated by Django 6.0.5 on 2026-07-04 22:29
import wagtail.fields
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('associations', '0028_alter_associationindex_body_and_more'),
]
operations = [
migrations.AlterField(
model_name='associationindex',
name='body',
field=wagtail.fields.StreamField([('paragraph', 0), ('image', 4), ('image_slider', 8), ('horizontal_rule', 10), ('featured', 18), ('page_section_navigation', 19), ('accordion', 23), ('fact_box', 26), ('schedule', 33), ('embed', 34), ('raw_html', 35)], block_lookup={0: ('wagtail.blocks.RichTextBlock', (), {'label': 'Rik tekst'}), 1: ('wagtail.images.blocks.ImageChooserBlock', (), {'label': 'Bilde'}), 2: ('wagtail.blocks.ChoiceBlock', [], {'choices': [('fullwidth', 'Fullbredde'), ('bleed', 'Utfallende'), ('original', 'Uendret størrelse')], 'icon': 'cup', 'label': 'Bildeformat'}), 3: ('wagtail.blocks.CharBlock', (), {'label': 'Bildetekst', 'max_length': 512, 'required': False}), 4: ('wagtail.blocks.StructBlock', [[('image', 1), ('image_format', 2), ('text', 3)]], {}), 5: ('wagtail.blocks.CharBlock', (), {'label': 'Tekst', 'max_length': 512, 'required': False}), 6: ('wagtail.blocks.StructBlock', [[('image', 1), ('text', 5)]], {}), 7: ('wagtail.blocks.ListBlock', (6,), {'label': 'Bilder', 'min_num': 1}), 8: ('wagtail.blocks.StructBlock', [[('images', 7)]], {}), 9: ('wagtail.blocks.ChoiceBlock', [], {'choices': [('deepBrick', 'Dyp tegl'), ('neufPink', 'Griserosa'), ('goldenOrange', 'Gyllen oransje'), ('goldenBeige', 'Gyllen beige'), ('chateauBlue', 'Slottsblå')], 'label': 'Farge', 'required': False}), 10: ('wagtail.blocks.StructBlock', [[('color', 9)]], {}), 11: ('wagtail.blocks.CharBlock', (), {'label': 'Tittel', 'max_length': 64, 'required': True}), 12: ('wagtail.blocks.RichTextBlock', (), {'features': ['bold', 'italic', 'link'], 'label': 'Tekst', 'required': True}), 13: ('wagtail.blocks.PageChooserBlock', (), {'header': 'Fremhevet side', 'required': True}), 14: ('wagtail.blocks.CharBlock', (), {'default': 'Les mer', 'help_text': 'Lenketeksten som tar deg videre til siden. Tips: Ikke start med "Trykk her"', 'label': 'Lenketekst', 'max_length': 64, 'required': True}), 15: ('wagtail.blocks.ChoiceBlock', [], {'choices': [('betongGray', 'Betonggrå'), ('deepBrick', 'Dyp tegl'), ('neufPink', 'Griserosa'), ('goldenOrange', 'Gyllen oransje'), ('goldenBeige', 'Gyllen beige'), ('chateauBlue', 'Slottsblå')], 'label': 'Bakgrunnsfarge'}), 16: ('wagtail.blocks.ChoiceBlock', [], {'choices': [('left', 'Venstre'), ('right', 'Høyre')], 'label': 'Bildeplassering'}), 17: ('wagtail.images.blocks.ImageChooserBlock', (), {'header': 'Overstyr bilde', 'help_text': 'Bildet som er tilknyttet undersiden du vil fremheve, vil automatisk brukes. Om det mangler eller du vil overstyre hvilket bilde som et brukes, kan du velge et her.', 'required': False}), 18: ('wagtail.blocks.StructBlock', [[('title', 11), ('text', 12), ('featured_page', 13), ('link_text', 14), ('background_color', 15), ('image_position', 16), ('featured_image_override', 17)]], {}), 19: ('dnscms.blocks.PageSectionNavigationBlock', (), {}), 20: ('wagtail.blocks.CharBlock', (), {'label': 'Overskrift', 'max_length': 64, 'required': True}), 21: ('wagtail.blocks.StructBlock', [[('image', 1), ('image_format', 2), ('text', 3)]], {'label': 'Bilde'}), 22: ('wagtail.blocks.StreamBlock', [[('paragraph', 0), ('image', 21)]], {}), 23: ('wagtail.blocks.StructBlock', [[('heading', 20), ('body', 22)]], {}), 24: ('wagtail.blocks.ChoiceBlock', [], {'choices': [('betongGray', 'Betonggrå'), ('deepBrick', 'Dyp tegl'), ('neufPink', 'Griserosa'), ('goldenOrange', 'Gyllen oransje'), ('goldenBeige', 'Gyllen beige'), ('chateauBlue', 'Slottsblå')], 'label': 'Bakgrunnsfarge', 'required': False}), 25: ('wagtail.blocks.RichTextBlock', (), {'features': ['bold', 'italic', 'link', 'ol', 'ul', 'h2', 'h3'], 'label': 'Innhold'}), 26: ('wagtail.blocks.StructBlock', [[('background_color', 24), ('body', 25)]], {}), 27: ('wagtail.blocks.CharBlock', (), {'help_text': 'Overskrift for timeplanen (valgfritt)', 'label': 'Tittel', 'max_length': 64, 'required': False}), 28: ('wagtail.blocks.CharBlock', (), {'help_text': 'Fritekst, f.eks. "13:00", "20:00 23:00" eller "23.06"', 'label': 'Tidspunkt/dato', 'max_length': 64, 'required': True}), 29: ('wagtail.blocks.RichTextBlock', (), {'features': ['bold', 'italic', 'link'], 'help_text': 'Kort tittel på én linje. Du kan legge inn lenker.', 'label': 'Tittel', 'required': True}), 30: ('wagtail.blocks.RichTextBlock', (), {'features': ['bold', 'italic', 'link'], 'help_text': 'Valgfri tilleggsinformasjon.', 'label': 'Beskrivelse', 'required': False}), 31: ('wagtail.blocks.StructBlock', [[('time', 28), ('title', 29), ('description', 30)]], {}), 32: ('wagtail.blocks.ListBlock', (31,), {'label': 'Punkter', 'min_num': 1}), 33: ('wagtail.blocks.StructBlock', [[('title', 27), ('items', 32)]], {}), 34: ('wagtail.embeds.blocks.EmbedBlock', (), {}), 35: ('wagtail.blocks.RawHTMLBlock', (), {})}, default=[('paragraph', '')]),
),
migrations.AlterField(
model_name='associationpage',
name='body',
field=wagtail.fields.StreamField([('paragraph', 0), ('image', 4), ('image_slider', 8), ('horizontal_rule', 10), ('featured', 18), ('page_section_navigation', 19), ('accordion', 23), ('fact_box', 26), ('schedule', 33), ('embed', 34), ('raw_html', 35)], block_lookup={0: ('wagtail.blocks.RichTextBlock', (), {'label': 'Rik tekst'}), 1: ('wagtail.images.blocks.ImageChooserBlock', (), {'label': 'Bilde'}), 2: ('wagtail.blocks.ChoiceBlock', [], {'choices': [('fullwidth', 'Fullbredde'), ('bleed', 'Utfallende'), ('original', 'Uendret størrelse')], 'icon': 'cup', 'label': 'Bildeformat'}), 3: ('wagtail.blocks.CharBlock', (), {'label': 'Bildetekst', 'max_length': 512, 'required': False}), 4: ('wagtail.blocks.StructBlock', [[('image', 1), ('image_format', 2), ('text', 3)]], {}), 5: ('wagtail.blocks.CharBlock', (), {'label': 'Tekst', 'max_length': 512, 'required': False}), 6: ('wagtail.blocks.StructBlock', [[('image', 1), ('text', 5)]], {}), 7: ('wagtail.blocks.ListBlock', (6,), {'label': 'Bilder', 'min_num': 1}), 8: ('wagtail.blocks.StructBlock', [[('images', 7)]], {}), 9: ('wagtail.blocks.ChoiceBlock', [], {'choices': [('deepBrick', 'Dyp tegl'), ('neufPink', 'Griserosa'), ('goldenOrange', 'Gyllen oransje'), ('goldenBeige', 'Gyllen beige'), ('chateauBlue', 'Slottsblå')], 'label': 'Farge', 'required': False}), 10: ('wagtail.blocks.StructBlock', [[('color', 9)]], {}), 11: ('wagtail.blocks.CharBlock', (), {'label': 'Tittel', 'max_length': 64, 'required': True}), 12: ('wagtail.blocks.RichTextBlock', (), {'features': ['bold', 'italic', 'link'], 'label': 'Tekst', 'required': True}), 13: ('wagtail.blocks.PageChooserBlock', (), {'header': 'Fremhevet side', 'required': True}), 14: ('wagtail.blocks.CharBlock', (), {'default': 'Les mer', 'help_text': 'Lenketeksten som tar deg videre til siden. Tips: Ikke start med "Trykk her"', 'label': 'Lenketekst', 'max_length': 64, 'required': True}), 15: ('wagtail.blocks.ChoiceBlock', [], {'choices': [('betongGray', 'Betonggrå'), ('deepBrick', 'Dyp tegl'), ('neufPink', 'Griserosa'), ('goldenOrange', 'Gyllen oransje'), ('goldenBeige', 'Gyllen beige'), ('chateauBlue', 'Slottsblå')], 'label': 'Bakgrunnsfarge'}), 16: ('wagtail.blocks.ChoiceBlock', [], {'choices': [('left', 'Venstre'), ('right', 'Høyre')], 'label': 'Bildeplassering'}), 17: ('wagtail.images.blocks.ImageChooserBlock', (), {'header': 'Overstyr bilde', 'help_text': 'Bildet som er tilknyttet undersiden du vil fremheve, vil automatisk brukes. Om det mangler eller du vil overstyre hvilket bilde som et brukes, kan du velge et her.', 'required': False}), 18: ('wagtail.blocks.StructBlock', [[('title', 11), ('text', 12), ('featured_page', 13), ('link_text', 14), ('background_color', 15), ('image_position', 16), ('featured_image_override', 17)]], {}), 19: ('dnscms.blocks.PageSectionNavigationBlock', (), {}), 20: ('wagtail.blocks.CharBlock', (), {'label': 'Overskrift', 'max_length': 64, 'required': True}), 21: ('wagtail.blocks.StructBlock', [[('image', 1), ('image_format', 2), ('text', 3)]], {'label': 'Bilde'}), 22: ('wagtail.blocks.StreamBlock', [[('paragraph', 0), ('image', 21)]], {}), 23: ('wagtail.blocks.StructBlock', [[('heading', 20), ('body', 22)]], {}), 24: ('wagtail.blocks.ChoiceBlock', [], {'choices': [('betongGray', 'Betonggrå'), ('deepBrick', 'Dyp tegl'), ('neufPink', 'Griserosa'), ('goldenOrange', 'Gyllen oransje'), ('goldenBeige', 'Gyllen beige'), ('chateauBlue', 'Slottsblå')], 'label': 'Bakgrunnsfarge', 'required': False}), 25: ('wagtail.blocks.RichTextBlock', (), {'features': ['bold', 'italic', 'link', 'ol', 'ul', 'h2', 'h3'], 'label': 'Innhold'}), 26: ('wagtail.blocks.StructBlock', [[('background_color', 24), ('body', 25)]], {}), 27: ('wagtail.blocks.CharBlock', (), {'help_text': 'Overskrift for timeplanen (valgfritt)', 'label': 'Tittel', 'max_length': 64, 'required': False}), 28: ('wagtail.blocks.CharBlock', (), {'help_text': 'Fritekst, f.eks. "13:00", "20:00 23:00" eller "23.06"', 'label': 'Tidspunkt/dato', 'max_length': 64, 'required': True}), 29: ('wagtail.blocks.RichTextBlock', (), {'features': ['bold', 'italic', 'link'], 'help_text': 'Kort tittel på én linje. Du kan legge inn lenker.', 'label': 'Tittel', 'required': True}), 30: ('wagtail.blocks.RichTextBlock', (), {'features': ['bold', 'italic', 'link'], 'help_text': 'Valgfri tilleggsinformasjon.', 'label': 'Beskrivelse', 'required': False}), 31: ('wagtail.blocks.StructBlock', [[('time', 28), ('title', 29), ('description', 30)]], {}), 32: ('wagtail.blocks.ListBlock', (31,), {'label': 'Punkter', 'min_num': 1}), 33: ('wagtail.blocks.StructBlock', [[('title', 27), ('items', 32)]], {}), 34: ('wagtail.embeds.blocks.EmbedBlock', (), {}), 35: ('wagtail.blocks.RawHTMLBlock', (), {})}, default=[('paragraph', '')]),
),
]
File diff suppressed because one or more lines are too long
-48
View File
@@ -1,4 +1,3 @@
from django.core.exceptions import ValidationError
from grapple.helpers import register_streamfield_block
from grapple.models import (
GraphQLImage,
@@ -8,7 +7,6 @@ from grapple.models import (
GraphQLString,
)
from wagtail import blocks
from wagtail.blocks import StructBlockValidationError
from wagtail.embeds.blocks import EmbedBlock
from wagtail.images.blocks import ImageChooserBlock
@@ -283,51 +281,6 @@ class ScheduleBlock(blocks.StructBlock):
label = "Timeplan"
@register_streamfield_block
class ButtonLinkBlock(blocks.StructBlock):
text = blocks.CharBlock(
max_length=64,
required=True,
label="Tekst",
help_text="Teksten som vises på knappen.",
)
page = blocks.PageChooserBlock(
required=False,
label="Intern side",
help_text="Velg en underside på denne nettsiden.",
)
anchor = blocks.CharBlock(
max_length=64,
required=False,
label="Anker",
help_text="Anker på siden, uten «#» (valgfritt)",
)
external_url = blocks.URLBlock(
required=False,
label="Ekstern lenke",
)
graphql_fields = [
GraphQLString("text", required=True),
GraphQLPage("page"),
GraphQLString("anchor"),
GraphQLString("external_url"),
]
def clean(self, value):
result = super().clean(value)
if value.get("page") and value.get("external_url"):
error = ValidationError(
"Velg enten en intern side eller en ekstern lenke, ikke begge."
)
raise StructBlockValidationError(block_errors={"page": error, "external_url": error})
return result
class Meta:
icon = "link"
label = "Knapp med lenke"
BASE_BLOCKS = [
("paragraph", blocks.RichTextBlock(label="Rik tekst")),
("image", ImageWithTextBlock()),
@@ -338,7 +291,6 @@ BASE_BLOCKS = [
("accordion", AccordionBlock()),
("fact_box", FactBoxBlock()),
("schedule", ScheduleBlock()),
("button_link", ButtonLinkBlock()),
("embed", EmbedBlock()),
("raw_html", blocks.RawHTMLBlock()),
]
+92
View File
@@ -0,0 +1,92 @@
"""Inline "button" link for Draftail rich text fields.
Stored as `<a linktype="button" data-href="...">label</a>`. The linktype (not a
plain href) avoids colliding with the built-in link feature, and lets
expand_db_html rewrite it to `<a class="button" href="...">` for the front end.
Wired up in wagtail_hooks.py.
"""
from django.urls import reverse_lazy
from django.utils.html import escape
from draftjs_exporter.dom import DOM
from wagtail.admin.rich_text.converters.html_to_contentstate import (
InlineEntityElementHandler,
)
from wagtail.admin.rich_text.editors.draftail import features as draftail_features
from wagtail.rich_text import LinkHandler
FEATURE_NAME = "button-link"
ENTITY_TYPE = "BUTTON"
def button_link_entity_decorator(props):
"""ContentState to DB HTML (keeps just the chosen URL)."""
return DOM.create_element(
"a",
{"linktype": "button", "data-href": props.get("url", "")},
props["children"],
)
class ButtonLinkEntityElementHandler(InlineEntityElementHandler):
"""DB HTML to ContentState."""
mutability = "MUTABLE"
def get_attribute_data(self, attrs):
return {"url": attrs.get("data-href", "")}
class ButtonLinkRichTextHandler(LinkHandler):
"""DB HTML to front-end HTML (expand_db_html)."""
identifier = "button"
@classmethod
def expand_db_attributes(cls, attrs):
href = escape(attrs.get("data-href", ""))
return f'<a class="button" href="{href}">'
def register_button_link_feature(features):
features.register_editor_plugin(
"draftail",
FEATURE_NAME,
draftail_features.EntityFeature(
{
"type": ENTITY_TYPE,
"icon": "arrow-right-full",
"description": "Knapp",
# Reuse the link chooser modal + payload, so buttons work like links.
"attributes": ["url", "id", "parentId"],
"allowlist": {"href": "^(http:|https:|mailto:|tel:|#|/|undefined$)"},
"chooserUrls": {
"pageChooser": reverse_lazy("wagtailadmin_choose_page"),
"externalLinkChooser": reverse_lazy("wagtailadmin_choose_page_external_link"),
"emailLinkChooser": reverse_lazy("wagtailadmin_choose_page_email_link"),
"phoneLinkChooser": reverse_lazy("wagtailadmin_choose_page_phone_link"),
"anchorLinkChooser": reverse_lazy("wagtailadmin_choose_page_anchor_link"),
},
},
js=[
# Gives LinkModalWorkflowSource its PAGE_CHOOSER_MODAL_ONLOAD_HANDLERS.
"wagtailadmin/js/page-chooser-modal.js",
"js/draftail-button-link.js",
],
css={"all": ["css/draftail-button-link.css"]},
),
)
features.register_converter_rule(
"contentstate",
FEATURE_NAME,
{
"from_database_format": {
'a[linktype="button"]': ButtonLinkEntityElementHandler(ENTITY_TYPE),
},
"to_database_format": {
"entity_decorators": {ENTITY_TYPE: button_link_entity_decorator},
},
},
)
features.register_link_type(ButtonLinkRichTextHandler)
features.default_features.append(FEATURE_NAME)
@@ -0,0 +1,17 @@
/* Styles the button-link entity as a button in the editor. Front end uses the global .button class. */
.button-link-entity .TooltipEntity {
display: inline-flex;
align-items: center;
gap: 0.25em;
padding: 0.1em 0.7em;
border: 1.5px solid currentColor;
border-radius: 999px;
font-weight: 600;
text-decoration: none;
line-height: 1.4;
}
.button-link-entity .TooltipEntity__icon {
width: 0.85em;
height: 0.85em;
}
@@ -0,0 +1,40 @@
/*
* Draftail plugin for the "button-link" feature (see dnscms/rich_text.py).
* Reuses Wagtail's link chooser (source) and edit/remove tooltip (decorator),
* so buttons insert and edit like normal links. No build step: uses Wagtail's
* globals and React.createElement.
*/
(function () {
"use strict";
var draftail = window.draftail;
var React = window.React;
var Icon = window.wagtail.components.Icon;
var TooltipEntity = draftail.TooltipEntity;
// Wrap TooltipEntity so CSS can style it as a button
function ButtonLink(props) {
var url = props.contentState.getEntity(props.entityKey).getData().url || null;
return React.createElement(
"span",
{ className: "button-link-entity" },
React.createElement(
TooltipEntity,
Object.assign({}, props, {
icon: React.createElement(Icon, { name: "arrow-right-full" }),
label: url || "",
url: url,
})
)
);
}
draftail.registerPlugin(
{
type: "BUTTON",
source: draftail.LinkModalWorkflowSource,
decorator: ButtonLink,
},
"entityTypes"
);
})();
+7
View File
@@ -5,12 +5,19 @@ from wagtail import hooks
from wagtail.models import Page
from wagtail.search.backends import get_search_backend
from dnscms.rich_text import register_button_link_feature
@hooks.register("register_rich_text_features")
def enable_additional_rich_text_features(features):
features.default_features.extend(["h5", "h6", "blockquote"])
@hooks.register("register_rich_text_features")
def register_button_link(features):
register_button_link_feature(features)
@hooks.register("register_schema_query")
def override_search_resolver(query_mixins):
"""
@@ -0,0 +1,19 @@
# Generated by Django 6.0.5 on 2026-07-04 22:29
import wagtail.fields
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('events', '0057_alter_eventpage_body'),
]
operations = [
migrations.AlterField(
model_name='eventpage',
name='body',
field=wagtail.fields.StreamField([('paragraph', 0), ('image', 4), ('image_slider', 8), ('horizontal_rule', 10), ('featured', 18), ('page_section_navigation', 19), ('accordion', 23), ('fact_box', 26), ('schedule', 33), ('embed', 34), ('raw_html', 35)], block_lookup={0: ('wagtail.blocks.RichTextBlock', (), {'label': 'Rik tekst'}), 1: ('wagtail.images.blocks.ImageChooserBlock', (), {'label': 'Bilde'}), 2: ('wagtail.blocks.ChoiceBlock', [], {'choices': [('fullwidth', 'Fullbredde'), ('bleed', 'Utfallende'), ('original', 'Uendret størrelse')], 'icon': 'cup', 'label': 'Bildeformat'}), 3: ('wagtail.blocks.CharBlock', (), {'label': 'Bildetekst', 'max_length': 512, 'required': False}), 4: ('wagtail.blocks.StructBlock', [[('image', 1), ('image_format', 2), ('text', 3)]], {}), 5: ('wagtail.blocks.CharBlock', (), {'label': 'Tekst', 'max_length': 512, 'required': False}), 6: ('wagtail.blocks.StructBlock', [[('image', 1), ('text', 5)]], {}), 7: ('wagtail.blocks.ListBlock', (6,), {'label': 'Bilder', 'min_num': 1}), 8: ('wagtail.blocks.StructBlock', [[('images', 7)]], {}), 9: ('wagtail.blocks.ChoiceBlock', [], {'choices': [('deepBrick', 'Dyp tegl'), ('neufPink', 'Griserosa'), ('goldenOrange', 'Gyllen oransje'), ('goldenBeige', 'Gyllen beige'), ('chateauBlue', 'Slottsblå')], 'label': 'Farge', 'required': False}), 10: ('wagtail.blocks.StructBlock', [[('color', 9)]], {}), 11: ('wagtail.blocks.CharBlock', (), {'label': 'Tittel', 'max_length': 64, 'required': True}), 12: ('wagtail.blocks.RichTextBlock', (), {'features': ['bold', 'italic', 'link'], 'label': 'Tekst', 'required': True}), 13: ('wagtail.blocks.PageChooserBlock', (), {'header': 'Fremhevet side', 'required': True}), 14: ('wagtail.blocks.CharBlock', (), {'default': 'Les mer', 'help_text': 'Lenketeksten som tar deg videre til siden. Tips: Ikke start med "Trykk her"', 'label': 'Lenketekst', 'max_length': 64, 'required': True}), 15: ('wagtail.blocks.ChoiceBlock', [], {'choices': [('betongGray', 'Betonggrå'), ('deepBrick', 'Dyp tegl'), ('neufPink', 'Griserosa'), ('goldenOrange', 'Gyllen oransje'), ('goldenBeige', 'Gyllen beige'), ('chateauBlue', 'Slottsblå')], 'label': 'Bakgrunnsfarge'}), 16: ('wagtail.blocks.ChoiceBlock', [], {'choices': [('left', 'Venstre'), ('right', 'Høyre')], 'label': 'Bildeplassering'}), 17: ('wagtail.images.blocks.ImageChooserBlock', (), {'header': 'Overstyr bilde', 'help_text': 'Bildet som er tilknyttet undersiden du vil fremheve, vil automatisk brukes. Om det mangler eller du vil overstyre hvilket bilde som et brukes, kan du velge et her.', 'required': False}), 18: ('wagtail.blocks.StructBlock', [[('title', 11), ('text', 12), ('featured_page', 13), ('link_text', 14), ('background_color', 15), ('image_position', 16), ('featured_image_override', 17)]], {}), 19: ('dnscms.blocks.PageSectionNavigationBlock', (), {}), 20: ('wagtail.blocks.CharBlock', (), {'label': 'Overskrift', 'max_length': 64, 'required': True}), 21: ('wagtail.blocks.StructBlock', [[('image', 1), ('image_format', 2), ('text', 3)]], {'label': 'Bilde'}), 22: ('wagtail.blocks.StreamBlock', [[('paragraph', 0), ('image', 21)]], {}), 23: ('wagtail.blocks.StructBlock', [[('heading', 20), ('body', 22)]], {}), 24: ('wagtail.blocks.ChoiceBlock', [], {'choices': [('betongGray', 'Betonggrå'), ('deepBrick', 'Dyp tegl'), ('neufPink', 'Griserosa'), ('goldenOrange', 'Gyllen oransje'), ('goldenBeige', 'Gyllen beige'), ('chateauBlue', 'Slottsblå')], 'label': 'Bakgrunnsfarge', 'required': False}), 25: ('wagtail.blocks.RichTextBlock', (), {'features': ['bold', 'italic', 'link', 'ol', 'ul', 'h2', 'h3'], 'label': 'Innhold'}), 26: ('wagtail.blocks.StructBlock', [[('background_color', 24), ('body', 25)]], {}), 27: ('wagtail.blocks.CharBlock', (), {'help_text': 'Overskrift for timeplanen (valgfritt)', 'label': 'Tittel', 'max_length': 64, 'required': False}), 28: ('wagtail.blocks.CharBlock', (), {'help_text': 'Fritekst, f.eks. "13:00", "20:00 23:00" eller "23.06"', 'label': 'Tidspunkt/dato', 'max_length': 64, 'required': True}), 29: ('wagtail.blocks.RichTextBlock', (), {'features': ['bold', 'italic', 'link'], 'help_text': 'Kort tittel på én linje. Du kan legge inn lenker.', 'label': 'Tittel', 'required': True}), 30: ('wagtail.blocks.RichTextBlock', (), {'features': ['bold', 'italic', 'link'], 'help_text': 'Valgfri tilleggsinformasjon.', 'label': 'Beskrivelse', 'required': False}), 31: ('wagtail.blocks.StructBlock', [[('time', 28), ('title', 29), ('description', 30)]], {}), 32: ('wagtail.blocks.ListBlock', (31,), {'label': 'Punkter', 'min_num': 1}), 33: ('wagtail.blocks.StructBlock', [[('title', 27), ('items', 32)]], {}), 34: ('wagtail.embeds.blocks.EmbedBlock', (), {}), 35: ('wagtail.blocks.RawHTMLBlock', (), {})}, default=[('paragraph', '')]),
),
]
File diff suppressed because one or more lines are too long
@@ -0,0 +1,19 @@
# Generated by Django 6.0.5 on 2026-07-04 22:29
import wagtail.fields
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('news', '0021_alter_newspage_body'),
]
operations = [
migrations.AlterField(
model_name='newspage',
name='body',
field=wagtail.fields.StreamField([('paragraph', 0), ('image', 4), ('image_slider', 8), ('horizontal_rule', 10), ('featured', 18), ('page_section_navigation', 19), ('accordion', 23), ('fact_box', 26), ('schedule', 33), ('embed', 34), ('raw_html', 35)], block_lookup={0: ('wagtail.blocks.RichTextBlock', (), {'label': 'Rik tekst'}), 1: ('wagtail.images.blocks.ImageChooserBlock', (), {'label': 'Bilde'}), 2: ('wagtail.blocks.ChoiceBlock', [], {'choices': [('fullwidth', 'Fullbredde'), ('bleed', 'Utfallende'), ('original', 'Uendret størrelse')], 'icon': 'cup', 'label': 'Bildeformat'}), 3: ('wagtail.blocks.CharBlock', (), {'label': 'Bildetekst', 'max_length': 512, 'required': False}), 4: ('wagtail.blocks.StructBlock', [[('image', 1), ('image_format', 2), ('text', 3)]], {}), 5: ('wagtail.blocks.CharBlock', (), {'label': 'Tekst', 'max_length': 512, 'required': False}), 6: ('wagtail.blocks.StructBlock', [[('image', 1), ('text', 5)]], {}), 7: ('wagtail.blocks.ListBlock', (6,), {'label': 'Bilder', 'min_num': 1}), 8: ('wagtail.blocks.StructBlock', [[('images', 7)]], {}), 9: ('wagtail.blocks.ChoiceBlock', [], {'choices': [('deepBrick', 'Dyp tegl'), ('neufPink', 'Griserosa'), ('goldenOrange', 'Gyllen oransje'), ('goldenBeige', 'Gyllen beige'), ('chateauBlue', 'Slottsblå')], 'label': 'Farge', 'required': False}), 10: ('wagtail.blocks.StructBlock', [[('color', 9)]], {}), 11: ('wagtail.blocks.CharBlock', (), {'label': 'Tittel', 'max_length': 64, 'required': True}), 12: ('wagtail.blocks.RichTextBlock', (), {'features': ['bold', 'italic', 'link'], 'label': 'Tekst', 'required': True}), 13: ('wagtail.blocks.PageChooserBlock', (), {'header': 'Fremhevet side', 'required': True}), 14: ('wagtail.blocks.CharBlock', (), {'default': 'Les mer', 'help_text': 'Lenketeksten som tar deg videre til siden. Tips: Ikke start med "Trykk her"', 'label': 'Lenketekst', 'max_length': 64, 'required': True}), 15: ('wagtail.blocks.ChoiceBlock', [], {'choices': [('betongGray', 'Betonggrå'), ('deepBrick', 'Dyp tegl'), ('neufPink', 'Griserosa'), ('goldenOrange', 'Gyllen oransje'), ('goldenBeige', 'Gyllen beige'), ('chateauBlue', 'Slottsblå')], 'label': 'Bakgrunnsfarge'}), 16: ('wagtail.blocks.ChoiceBlock', [], {'choices': [('left', 'Venstre'), ('right', 'Høyre')], 'label': 'Bildeplassering'}), 17: ('wagtail.images.blocks.ImageChooserBlock', (), {'header': 'Overstyr bilde', 'help_text': 'Bildet som er tilknyttet undersiden du vil fremheve, vil automatisk brukes. Om det mangler eller du vil overstyre hvilket bilde som et brukes, kan du velge et her.', 'required': False}), 18: ('wagtail.blocks.StructBlock', [[('title', 11), ('text', 12), ('featured_page', 13), ('link_text', 14), ('background_color', 15), ('image_position', 16), ('featured_image_override', 17)]], {}), 19: ('dnscms.blocks.PageSectionNavigationBlock', (), {}), 20: ('wagtail.blocks.CharBlock', (), {'label': 'Overskrift', 'max_length': 64, 'required': True}), 21: ('wagtail.blocks.StructBlock', [[('image', 1), ('image_format', 2), ('text', 3)]], {'label': 'Bilde'}), 22: ('wagtail.blocks.StreamBlock', [[('paragraph', 0), ('image', 21)]], {}), 23: ('wagtail.blocks.StructBlock', [[('heading', 20), ('body', 22)]], {}), 24: ('wagtail.blocks.ChoiceBlock', [], {'choices': [('betongGray', 'Betonggrå'), ('deepBrick', 'Dyp tegl'), ('neufPink', 'Griserosa'), ('goldenOrange', 'Gyllen oransje'), ('goldenBeige', 'Gyllen beige'), ('chateauBlue', 'Slottsblå')], 'label': 'Bakgrunnsfarge', 'required': False}), 25: ('wagtail.blocks.RichTextBlock', (), {'features': ['bold', 'italic', 'link', 'ol', 'ul', 'h2', 'h3'], 'label': 'Innhold'}), 26: ('wagtail.blocks.StructBlock', [[('background_color', 24), ('body', 25)]], {}), 27: ('wagtail.blocks.CharBlock', (), {'help_text': 'Overskrift for timeplanen (valgfritt)', 'label': 'Tittel', 'max_length': 64, 'required': False}), 28: ('wagtail.blocks.CharBlock', (), {'help_text': 'Fritekst, f.eks. "13:00", "20:00 23:00" eller "23.06"', 'label': 'Tidspunkt/dato', 'max_length': 64, 'required': True}), 29: ('wagtail.blocks.RichTextBlock', (), {'features': ['bold', 'italic', 'link'], 'help_text': 'Kort tittel på én linje. Du kan legge inn lenker.', 'label': 'Tittel', 'required': True}), 30: ('wagtail.blocks.RichTextBlock', (), {'features': ['bold', 'italic', 'link'], 'help_text': 'Valgfri tilleggsinformasjon.', 'label': 'Beskrivelse', 'required': False}), 31: ('wagtail.blocks.StructBlock', [[('time', 28), ('title', 29), ('description', 30)]], {}), 32: ('wagtail.blocks.ListBlock', (31,), {'label': 'Punkter', 'min_num': 1}), 33: ('wagtail.blocks.StructBlock', [[('title', 27), ('items', 32)]], {}), 34: ('wagtail.embeds.blocks.EmbedBlock', (), {}), 35: ('wagtail.blocks.RawHTMLBlock', (), {})}, default=[('paragraph', '')]),
),
]
@@ -0,0 +1,19 @@
# Generated by Django 6.0.5 on 2026-07-04 22:29
import wagtail.fields
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('sponsors', '0003_alter_sponsorspage_body'),
]
operations = [
migrations.AlterField(
model_name='sponsorspage',
name='body',
field=wagtail.fields.StreamField([('paragraph', 0), ('image', 4), ('image_slider', 8), ('horizontal_rule', 10), ('featured', 18), ('page_section_navigation', 19), ('accordion', 23), ('fact_box', 26), ('schedule', 33), ('embed', 34), ('raw_html', 35)], block_lookup={0: ('wagtail.blocks.RichTextBlock', (), {'label': 'Rik tekst'}), 1: ('wagtail.images.blocks.ImageChooserBlock', (), {'label': 'Bilde'}), 2: ('wagtail.blocks.ChoiceBlock', [], {'choices': [('fullwidth', 'Fullbredde'), ('bleed', 'Utfallende'), ('original', 'Uendret størrelse')], 'icon': 'cup', 'label': 'Bildeformat'}), 3: ('wagtail.blocks.CharBlock', (), {'label': 'Bildetekst', 'max_length': 512, 'required': False}), 4: ('wagtail.blocks.StructBlock', [[('image', 1), ('image_format', 2), ('text', 3)]], {}), 5: ('wagtail.blocks.CharBlock', (), {'label': 'Tekst', 'max_length': 512, 'required': False}), 6: ('wagtail.blocks.StructBlock', [[('image', 1), ('text', 5)]], {}), 7: ('wagtail.blocks.ListBlock', (6,), {'label': 'Bilder', 'min_num': 1}), 8: ('wagtail.blocks.StructBlock', [[('images', 7)]], {}), 9: ('wagtail.blocks.ChoiceBlock', [], {'choices': [('deepBrick', 'Dyp tegl'), ('neufPink', 'Griserosa'), ('goldenOrange', 'Gyllen oransje'), ('goldenBeige', 'Gyllen beige'), ('chateauBlue', 'Slottsblå')], 'label': 'Farge', 'required': False}), 10: ('wagtail.blocks.StructBlock', [[('color', 9)]], {}), 11: ('wagtail.blocks.CharBlock', (), {'label': 'Tittel', 'max_length': 64, 'required': True}), 12: ('wagtail.blocks.RichTextBlock', (), {'features': ['bold', 'italic', 'link'], 'label': 'Tekst', 'required': True}), 13: ('wagtail.blocks.PageChooserBlock', (), {'header': 'Fremhevet side', 'required': True}), 14: ('wagtail.blocks.CharBlock', (), {'default': 'Les mer', 'help_text': 'Lenketeksten som tar deg videre til siden. Tips: Ikke start med "Trykk her"', 'label': 'Lenketekst', 'max_length': 64, 'required': True}), 15: ('wagtail.blocks.ChoiceBlock', [], {'choices': [('betongGray', 'Betonggrå'), ('deepBrick', 'Dyp tegl'), ('neufPink', 'Griserosa'), ('goldenOrange', 'Gyllen oransje'), ('goldenBeige', 'Gyllen beige'), ('chateauBlue', 'Slottsblå')], 'label': 'Bakgrunnsfarge'}), 16: ('wagtail.blocks.ChoiceBlock', [], {'choices': [('left', 'Venstre'), ('right', 'Høyre')], 'label': 'Bildeplassering'}), 17: ('wagtail.images.blocks.ImageChooserBlock', (), {'header': 'Overstyr bilde', 'help_text': 'Bildet som er tilknyttet undersiden du vil fremheve, vil automatisk brukes. Om det mangler eller du vil overstyre hvilket bilde som et brukes, kan du velge et her.', 'required': False}), 18: ('wagtail.blocks.StructBlock', [[('title', 11), ('text', 12), ('featured_page', 13), ('link_text', 14), ('background_color', 15), ('image_position', 16), ('featured_image_override', 17)]], {}), 19: ('dnscms.blocks.PageSectionNavigationBlock', (), {}), 20: ('wagtail.blocks.CharBlock', (), {'label': 'Overskrift', 'max_length': 64, 'required': True}), 21: ('wagtail.blocks.StructBlock', [[('image', 1), ('image_format', 2), ('text', 3)]], {'label': 'Bilde'}), 22: ('wagtail.blocks.StreamBlock', [[('paragraph', 0), ('image', 21)]], {}), 23: ('wagtail.blocks.StructBlock', [[('heading', 20), ('body', 22)]], {}), 24: ('wagtail.blocks.ChoiceBlock', [], {'choices': [('betongGray', 'Betonggrå'), ('deepBrick', 'Dyp tegl'), ('neufPink', 'Griserosa'), ('goldenOrange', 'Gyllen oransje'), ('goldenBeige', 'Gyllen beige'), ('chateauBlue', 'Slottsblå')], 'label': 'Bakgrunnsfarge', 'required': False}), 25: ('wagtail.blocks.RichTextBlock', (), {'features': ['bold', 'italic', 'link', 'ol', 'ul', 'h2', 'h3'], 'label': 'Innhold'}), 26: ('wagtail.blocks.StructBlock', [[('background_color', 24), ('body', 25)]], {}), 27: ('wagtail.blocks.CharBlock', (), {'help_text': 'Overskrift for timeplanen (valgfritt)', 'label': 'Tittel', 'max_length': 64, 'required': False}), 28: ('wagtail.blocks.CharBlock', (), {'help_text': 'Fritekst, f.eks. "13:00", "20:00 23:00" eller "23.06"', 'label': 'Tidspunkt/dato', 'max_length': 64, 'required': True}), 29: ('wagtail.blocks.RichTextBlock', (), {'features': ['bold', 'italic', 'link'], 'help_text': 'Kort tittel på én linje. Du kan legge inn lenker.', 'label': 'Tittel', 'required': True}), 30: ('wagtail.blocks.RichTextBlock', (), {'features': ['bold', 'italic', 'link'], 'help_text': 'Valgfri tilleggsinformasjon.', 'label': 'Beskrivelse', 'required': False}), 31: ('wagtail.blocks.StructBlock', [[('time', 28), ('title', 29), ('description', 30)]], {}), 32: ('wagtail.blocks.ListBlock', (31,), {'label': 'Punkter', 'min_num': 1}), 33: ('wagtail.blocks.StructBlock', [[('title', 27), ('items', 32)]], {}), 34: ('wagtail.embeds.blocks.EmbedBlock', (), {}), 35: ('wagtail.blocks.RawHTMLBlock', (), {})}),
),
]
File diff suppressed because one or more lines are too long
@@ -0,0 +1,29 @@
# Generated by Django 6.0.5 on 2026-07-04 22:29
import wagtail.fields
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('venues', '0027_alter_venueindex_body_alter_venuepage_body_and_more'),
]
operations = [
migrations.AlterField(
model_name='venueindex',
name='body',
field=wagtail.fields.StreamField([('paragraph', 0), ('image', 4), ('image_slider', 8), ('horizontal_rule', 10), ('featured', 18), ('page_section_navigation', 19), ('accordion', 23), ('fact_box', 26), ('schedule', 33), ('embed', 34), ('raw_html', 35)], block_lookup={0: ('wagtail.blocks.RichTextBlock', (), {'label': 'Rik tekst'}), 1: ('wagtail.images.blocks.ImageChooserBlock', (), {'label': 'Bilde'}), 2: ('wagtail.blocks.ChoiceBlock', [], {'choices': [('fullwidth', 'Fullbredde'), ('bleed', 'Utfallende'), ('original', 'Uendret størrelse')], 'icon': 'cup', 'label': 'Bildeformat'}), 3: ('wagtail.blocks.CharBlock', (), {'label': 'Bildetekst', 'max_length': 512, 'required': False}), 4: ('wagtail.blocks.StructBlock', [[('image', 1), ('image_format', 2), ('text', 3)]], {}), 5: ('wagtail.blocks.CharBlock', (), {'label': 'Tekst', 'max_length': 512, 'required': False}), 6: ('wagtail.blocks.StructBlock', [[('image', 1), ('text', 5)]], {}), 7: ('wagtail.blocks.ListBlock', (6,), {'label': 'Bilder', 'min_num': 1}), 8: ('wagtail.blocks.StructBlock', [[('images', 7)]], {}), 9: ('wagtail.blocks.ChoiceBlock', [], {'choices': [('deepBrick', 'Dyp tegl'), ('neufPink', 'Griserosa'), ('goldenOrange', 'Gyllen oransje'), ('goldenBeige', 'Gyllen beige'), ('chateauBlue', 'Slottsblå')], 'label': 'Farge', 'required': False}), 10: ('wagtail.blocks.StructBlock', [[('color', 9)]], {}), 11: ('wagtail.blocks.CharBlock', (), {'label': 'Tittel', 'max_length': 64, 'required': True}), 12: ('wagtail.blocks.RichTextBlock', (), {'features': ['bold', 'italic', 'link'], 'label': 'Tekst', 'required': True}), 13: ('wagtail.blocks.PageChooserBlock', (), {'header': 'Fremhevet side', 'required': True}), 14: ('wagtail.blocks.CharBlock', (), {'default': 'Les mer', 'help_text': 'Lenketeksten som tar deg videre til siden. Tips: Ikke start med "Trykk her"', 'label': 'Lenketekst', 'max_length': 64, 'required': True}), 15: ('wagtail.blocks.ChoiceBlock', [], {'choices': [('betongGray', 'Betonggrå'), ('deepBrick', 'Dyp tegl'), ('neufPink', 'Griserosa'), ('goldenOrange', 'Gyllen oransje'), ('goldenBeige', 'Gyllen beige'), ('chateauBlue', 'Slottsblå')], 'label': 'Bakgrunnsfarge'}), 16: ('wagtail.blocks.ChoiceBlock', [], {'choices': [('left', 'Venstre'), ('right', 'Høyre')], 'label': 'Bildeplassering'}), 17: ('wagtail.images.blocks.ImageChooserBlock', (), {'header': 'Overstyr bilde', 'help_text': 'Bildet som er tilknyttet undersiden du vil fremheve, vil automatisk brukes. Om det mangler eller du vil overstyre hvilket bilde som et brukes, kan du velge et her.', 'required': False}), 18: ('wagtail.blocks.StructBlock', [[('title', 11), ('text', 12), ('featured_page', 13), ('link_text', 14), ('background_color', 15), ('image_position', 16), ('featured_image_override', 17)]], {}), 19: ('dnscms.blocks.PageSectionNavigationBlock', (), {}), 20: ('wagtail.blocks.CharBlock', (), {'label': 'Overskrift', 'max_length': 64, 'required': True}), 21: ('wagtail.blocks.StructBlock', [[('image', 1), ('image_format', 2), ('text', 3)]], {'label': 'Bilde'}), 22: ('wagtail.blocks.StreamBlock', [[('paragraph', 0), ('image', 21)]], {}), 23: ('wagtail.blocks.StructBlock', [[('heading', 20), ('body', 22)]], {}), 24: ('wagtail.blocks.ChoiceBlock', [], {'choices': [('betongGray', 'Betonggrå'), ('deepBrick', 'Dyp tegl'), ('neufPink', 'Griserosa'), ('goldenOrange', 'Gyllen oransje'), ('goldenBeige', 'Gyllen beige'), ('chateauBlue', 'Slottsblå')], 'label': 'Bakgrunnsfarge', 'required': False}), 25: ('wagtail.blocks.RichTextBlock', (), {'features': ['bold', 'italic', 'link', 'ol', 'ul', 'h2', 'h3'], 'label': 'Innhold'}), 26: ('wagtail.blocks.StructBlock', [[('background_color', 24), ('body', 25)]], {}), 27: ('wagtail.blocks.CharBlock', (), {'help_text': 'Overskrift for timeplanen (valgfritt)', 'label': 'Tittel', 'max_length': 64, 'required': False}), 28: ('wagtail.blocks.CharBlock', (), {'help_text': 'Fritekst, f.eks. "13:00", "20:00 23:00" eller "23.06"', 'label': 'Tidspunkt/dato', 'max_length': 64, 'required': True}), 29: ('wagtail.blocks.RichTextBlock', (), {'features': ['bold', 'italic', 'link'], 'help_text': 'Kort tittel på én linje. Du kan legge inn lenker.', 'label': 'Tittel', 'required': True}), 30: ('wagtail.blocks.RichTextBlock', (), {'features': ['bold', 'italic', 'link'], 'help_text': 'Valgfri tilleggsinformasjon.', 'label': 'Beskrivelse', 'required': False}), 31: ('wagtail.blocks.StructBlock', [[('time', 28), ('title', 29), ('description', 30)]], {}), 32: ('wagtail.blocks.ListBlock', (31,), {'label': 'Punkter', 'min_num': 1}), 33: ('wagtail.blocks.StructBlock', [[('title', 27), ('items', 32)]], {}), 34: ('wagtail.embeds.blocks.EmbedBlock', (), {}), 35: ('wagtail.blocks.RawHTMLBlock', (), {})}, default=[('paragraph', '')]),
),
migrations.AlterField(
model_name='venuepage',
name='body',
field=wagtail.fields.StreamField([('paragraph', 0), ('image', 4), ('image_slider', 8), ('horizontal_rule', 10), ('featured', 18), ('page_section_navigation', 19), ('accordion', 23), ('fact_box', 26), ('schedule', 33), ('embed', 34), ('raw_html', 35)], block_lookup={0: ('wagtail.blocks.RichTextBlock', (), {'label': 'Rik tekst'}), 1: ('wagtail.images.blocks.ImageChooserBlock', (), {'label': 'Bilde'}), 2: ('wagtail.blocks.ChoiceBlock', [], {'choices': [('fullwidth', 'Fullbredde'), ('bleed', 'Utfallende'), ('original', 'Uendret størrelse')], 'icon': 'cup', 'label': 'Bildeformat'}), 3: ('wagtail.blocks.CharBlock', (), {'label': 'Bildetekst', 'max_length': 512, 'required': False}), 4: ('wagtail.blocks.StructBlock', [[('image', 1), ('image_format', 2), ('text', 3)]], {}), 5: ('wagtail.blocks.CharBlock', (), {'label': 'Tekst', 'max_length': 512, 'required': False}), 6: ('wagtail.blocks.StructBlock', [[('image', 1), ('text', 5)]], {}), 7: ('wagtail.blocks.ListBlock', (6,), {'label': 'Bilder', 'min_num': 1}), 8: ('wagtail.blocks.StructBlock', [[('images', 7)]], {}), 9: ('wagtail.blocks.ChoiceBlock', [], {'choices': [('deepBrick', 'Dyp tegl'), ('neufPink', 'Griserosa'), ('goldenOrange', 'Gyllen oransje'), ('goldenBeige', 'Gyllen beige'), ('chateauBlue', 'Slottsblå')], 'label': 'Farge', 'required': False}), 10: ('wagtail.blocks.StructBlock', [[('color', 9)]], {}), 11: ('wagtail.blocks.CharBlock', (), {'label': 'Tittel', 'max_length': 64, 'required': True}), 12: ('wagtail.blocks.RichTextBlock', (), {'features': ['bold', 'italic', 'link'], 'label': 'Tekst', 'required': True}), 13: ('wagtail.blocks.PageChooserBlock', (), {'header': 'Fremhevet side', 'required': True}), 14: ('wagtail.blocks.CharBlock', (), {'default': 'Les mer', 'help_text': 'Lenketeksten som tar deg videre til siden. Tips: Ikke start med "Trykk her"', 'label': 'Lenketekst', 'max_length': 64, 'required': True}), 15: ('wagtail.blocks.ChoiceBlock', [], {'choices': [('betongGray', 'Betonggrå'), ('deepBrick', 'Dyp tegl'), ('neufPink', 'Griserosa'), ('goldenOrange', 'Gyllen oransje'), ('goldenBeige', 'Gyllen beige'), ('chateauBlue', 'Slottsblå')], 'label': 'Bakgrunnsfarge'}), 16: ('wagtail.blocks.ChoiceBlock', [], {'choices': [('left', 'Venstre'), ('right', 'Høyre')], 'label': 'Bildeplassering'}), 17: ('wagtail.images.blocks.ImageChooserBlock', (), {'header': 'Overstyr bilde', 'help_text': 'Bildet som er tilknyttet undersiden du vil fremheve, vil automatisk brukes. Om det mangler eller du vil overstyre hvilket bilde som et brukes, kan du velge et her.', 'required': False}), 18: ('wagtail.blocks.StructBlock', [[('title', 11), ('text', 12), ('featured_page', 13), ('link_text', 14), ('background_color', 15), ('image_position', 16), ('featured_image_override', 17)]], {}), 19: ('dnscms.blocks.PageSectionNavigationBlock', (), {}), 20: ('wagtail.blocks.CharBlock', (), {'label': 'Overskrift', 'max_length': 64, 'required': True}), 21: ('wagtail.blocks.StructBlock', [[('image', 1), ('image_format', 2), ('text', 3)]], {'label': 'Bilde'}), 22: ('wagtail.blocks.StreamBlock', [[('paragraph', 0), ('image', 21)]], {}), 23: ('wagtail.blocks.StructBlock', [[('heading', 20), ('body', 22)]], {}), 24: ('wagtail.blocks.ChoiceBlock', [], {'choices': [('betongGray', 'Betonggrå'), ('deepBrick', 'Dyp tegl'), ('neufPink', 'Griserosa'), ('goldenOrange', 'Gyllen oransje'), ('goldenBeige', 'Gyllen beige'), ('chateauBlue', 'Slottsblå')], 'label': 'Bakgrunnsfarge', 'required': False}), 25: ('wagtail.blocks.RichTextBlock', (), {'features': ['bold', 'italic', 'link', 'ol', 'ul', 'h2', 'h3'], 'label': 'Innhold'}), 26: ('wagtail.blocks.StructBlock', [[('background_color', 24), ('body', 25)]], {}), 27: ('wagtail.blocks.CharBlock', (), {'help_text': 'Overskrift for timeplanen (valgfritt)', 'label': 'Tittel', 'max_length': 64, 'required': False}), 28: ('wagtail.blocks.CharBlock', (), {'help_text': 'Fritekst, f.eks. "13:00", "20:00 23:00" eller "23.06"', 'label': 'Tidspunkt/dato', 'max_length': 64, 'required': True}), 29: ('wagtail.blocks.RichTextBlock', (), {'features': ['bold', 'italic', 'link'], 'help_text': 'Kort tittel på én linje. Du kan legge inn lenker.', 'label': 'Tittel', 'required': True}), 30: ('wagtail.blocks.RichTextBlock', (), {'features': ['bold', 'italic', 'link'], 'help_text': 'Valgfri tilleggsinformasjon.', 'label': 'Beskrivelse', 'required': False}), 31: ('wagtail.blocks.StructBlock', [[('time', 28), ('title', 29), ('description', 30)]], {}), 32: ('wagtail.blocks.ListBlock', (31,), {'label': 'Punkter', 'min_num': 1}), 33: ('wagtail.blocks.StructBlock', [[('title', 27), ('items', 32)]], {}), 34: ('wagtail.embeds.blocks.EmbedBlock', (), {}), 35: ('wagtail.blocks.RawHTMLBlock', (), {})}, default=[('paragraph', '')]),
),
migrations.AlterField(
model_name='venuerentalindex',
name='body',
field=wagtail.fields.StreamField([('paragraph', 0), ('image', 4), ('image_slider', 8), ('horizontal_rule', 10), ('featured', 18), ('page_section_navigation', 19), ('accordion', 23), ('fact_box', 26), ('schedule', 33), ('embed', 34), ('raw_html', 35)], block_lookup={0: ('wagtail.blocks.RichTextBlock', (), {'label': 'Rik tekst'}), 1: ('wagtail.images.blocks.ImageChooserBlock', (), {'label': 'Bilde'}), 2: ('wagtail.blocks.ChoiceBlock', [], {'choices': [('fullwidth', 'Fullbredde'), ('bleed', 'Utfallende'), ('original', 'Uendret størrelse')], 'icon': 'cup', 'label': 'Bildeformat'}), 3: ('wagtail.blocks.CharBlock', (), {'label': 'Bildetekst', 'max_length': 512, 'required': False}), 4: ('wagtail.blocks.StructBlock', [[('image', 1), ('image_format', 2), ('text', 3)]], {}), 5: ('wagtail.blocks.CharBlock', (), {'label': 'Tekst', 'max_length': 512, 'required': False}), 6: ('wagtail.blocks.StructBlock', [[('image', 1), ('text', 5)]], {}), 7: ('wagtail.blocks.ListBlock', (6,), {'label': 'Bilder', 'min_num': 1}), 8: ('wagtail.blocks.StructBlock', [[('images', 7)]], {}), 9: ('wagtail.blocks.ChoiceBlock', [], {'choices': [('deepBrick', 'Dyp tegl'), ('neufPink', 'Griserosa'), ('goldenOrange', 'Gyllen oransje'), ('goldenBeige', 'Gyllen beige'), ('chateauBlue', 'Slottsblå')], 'label': 'Farge', 'required': False}), 10: ('wagtail.blocks.StructBlock', [[('color', 9)]], {}), 11: ('wagtail.blocks.CharBlock', (), {'label': 'Tittel', 'max_length': 64, 'required': True}), 12: ('wagtail.blocks.RichTextBlock', (), {'features': ['bold', 'italic', 'link'], 'label': 'Tekst', 'required': True}), 13: ('wagtail.blocks.PageChooserBlock', (), {'header': 'Fremhevet side', 'required': True}), 14: ('wagtail.blocks.CharBlock', (), {'default': 'Les mer', 'help_text': 'Lenketeksten som tar deg videre til siden. Tips: Ikke start med "Trykk her"', 'label': 'Lenketekst', 'max_length': 64, 'required': True}), 15: ('wagtail.blocks.ChoiceBlock', [], {'choices': [('betongGray', 'Betonggrå'), ('deepBrick', 'Dyp tegl'), ('neufPink', 'Griserosa'), ('goldenOrange', 'Gyllen oransje'), ('goldenBeige', 'Gyllen beige'), ('chateauBlue', 'Slottsblå')], 'label': 'Bakgrunnsfarge'}), 16: ('wagtail.blocks.ChoiceBlock', [], {'choices': [('left', 'Venstre'), ('right', 'Høyre')], 'label': 'Bildeplassering'}), 17: ('wagtail.images.blocks.ImageChooserBlock', (), {'header': 'Overstyr bilde', 'help_text': 'Bildet som er tilknyttet undersiden du vil fremheve, vil automatisk brukes. Om det mangler eller du vil overstyre hvilket bilde som et brukes, kan du velge et her.', 'required': False}), 18: ('wagtail.blocks.StructBlock', [[('title', 11), ('text', 12), ('featured_page', 13), ('link_text', 14), ('background_color', 15), ('image_position', 16), ('featured_image_override', 17)]], {}), 19: ('dnscms.blocks.PageSectionNavigationBlock', (), {}), 20: ('wagtail.blocks.CharBlock', (), {'label': 'Overskrift', 'max_length': 64, 'required': True}), 21: ('wagtail.blocks.StructBlock', [[('image', 1), ('image_format', 2), ('text', 3)]], {'label': 'Bilde'}), 22: ('wagtail.blocks.StreamBlock', [[('paragraph', 0), ('image', 21)]], {}), 23: ('wagtail.blocks.StructBlock', [[('heading', 20), ('body', 22)]], {}), 24: ('wagtail.blocks.ChoiceBlock', [], {'choices': [('betongGray', 'Betonggrå'), ('deepBrick', 'Dyp tegl'), ('neufPink', 'Griserosa'), ('goldenOrange', 'Gyllen oransje'), ('goldenBeige', 'Gyllen beige'), ('chateauBlue', 'Slottsblå')], 'label': 'Bakgrunnsfarge', 'required': False}), 25: ('wagtail.blocks.RichTextBlock', (), {'features': ['bold', 'italic', 'link', 'ol', 'ul', 'h2', 'h3'], 'label': 'Innhold'}), 26: ('wagtail.blocks.StructBlock', [[('background_color', 24), ('body', 25)]], {}), 27: ('wagtail.blocks.CharBlock', (), {'help_text': 'Overskrift for timeplanen (valgfritt)', 'label': 'Tittel', 'max_length': 64, 'required': False}), 28: ('wagtail.blocks.CharBlock', (), {'help_text': 'Fritekst, f.eks. "13:00", "20:00 23:00" eller "23.06"', 'label': 'Tidspunkt/dato', 'max_length': 64, 'required': True}), 29: ('wagtail.blocks.RichTextBlock', (), {'features': ['bold', 'italic', 'link'], 'help_text': 'Kort tittel på én linje. Du kan legge inn lenker.', 'label': 'Tittel', 'required': True}), 30: ('wagtail.blocks.RichTextBlock', (), {'features': ['bold', 'italic', 'link'], 'help_text': 'Valgfri tilleggsinformasjon.', 'label': 'Beskrivelse', 'required': False}), 31: ('wagtail.blocks.StructBlock', [[('time', 28), ('title', 29), ('description', 30)]], {}), 32: ('wagtail.blocks.ListBlock', (31,), {'label': 'Punkter', 'min_num': 1}), 33: ('wagtail.blocks.StructBlock', [[('title', 27), ('items', 32)]], {}), 34: ('wagtail.embeds.blocks.EmbedBlock', (), {}), 35: ('wagtail.blocks.RawHTMLBlock', (), {})}, default=[('paragraph', '')]),
),
]
+181 -2
View File
@@ -16,6 +16,7 @@
"date-fns": "^4.4.0",
"date-fns-tz": "^3.2.0",
"graphql": "^16.14.2",
"html-react-parser": "^6.1.3",
"next": "^16.2.9",
"nuqs": "^2.8.9",
"react": "19.2.7",
@@ -3963,7 +3964,7 @@
"version": "19.2.17",
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.17.tgz",
"integrity": "sha512-MXfmqaVPEVgkBT/aY0aGCkRWWtByiYQXo3xdQ8r5RzuFrPiRn8Gar2tQdXSUQ2GKV3bkXckek89V8wQBY2Q/Aw==",
"dev": true,
"devOptional": true,
"license": "MIT",
"dependencies": {
"csstype": "^3.2.2"
@@ -5490,7 +5491,7 @@
"version": "3.2.3",
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz",
"integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==",
"dev": true,
"devOptional": true,
"license": "MIT"
},
"node_modules/damerau-levenshtein": {
@@ -5740,6 +5741,73 @@
"node": ">=0.10.0"
}
},
"node_modules/dom-serializer": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-3.1.1.tgz",
"integrity": "sha512-4MEa38/QexBob6gFNwu+EGdWvhJ1OKuNwdYY3Y3NyeWDQfnGeDYQUDfIRzWu5B5gsv03so2Uxd28YC6zrsx3Lw==",
"license": "MIT",
"dependencies": {
"domelementtype": "^3.0.0",
"domhandler": "^6.0.0",
"entities": "^8.0.0"
},
"engines": {
"node": ">=20.19.0"
},
"funding": {
"type": "github",
"url": "https://github.com/cheeriojs/dom-serializer?sponsor=1"
}
},
"node_modules/domelementtype": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-3.0.0.tgz",
"integrity": "sha512-umCQid3jKbDmVjx8jGaW7uUykm4DEUeyV21hPxNMo2nV955DhUThwqyOIDtreepP31hl84X7G5U9ZfsWvIB3Pg==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/fb55"
}
],
"license": "BSD-2-Clause",
"engines": {
"node": ">=20.19.0"
}
},
"node_modules/domhandler": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/domhandler/-/domhandler-6.0.1.tgz",
"integrity": "sha512-gYzvtM72ZtxQO0T048kd6HWSbbGCNOUwcnfQ01cqIJ4X2IYKFFHZ5mKvrQETcFXxsRObZulDaKmy//R7TPtsBg==",
"license": "BSD-2-Clause",
"dependencies": {
"domelementtype": "^3.0.0"
},
"engines": {
"node": ">=20.19.0"
},
"funding": {
"type": "github",
"url": "https://github.com/fb55/domhandler?sponsor=1"
}
},
"node_modules/domutils": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/domutils/-/domutils-4.0.2.tgz",
"integrity": "sha512-qI4JLRKnSzqFqr7hAlS5xQDusBCjKSEG4t4+7aNrIQMHBcsC2TGEhuyABJdYkgSewL57PNLYEiibY2iPKhKpaA==",
"license": "BSD-2-Clause",
"dependencies": {
"dom-serializer": "^3.0.0",
"domelementtype": "^3.0.0",
"domhandler": "^6.0.0"
},
"engines": {
"node": ">=20.19.0"
},
"funding": {
"type": "github",
"url": "https://github.com/fb55/domutils?sponsor=1"
}
},
"node_modules/dot-prop": {
"version": "9.0.0",
"resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-9.0.0.tgz",
@@ -5819,6 +5887,18 @@
"node": ">=8"
}
},
"node_modules/entities": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/entities/-/entities-8.0.0.tgz",
"integrity": "sha512-zwfzJecQ/Uej6tusMqwAqU/6KL2XaB2VZ2Jg54Je6ahNBGNH6Ek6g3jjNCF0fG9EWQKGZNddNjU5F1ZQn/sBnA==",
"license": "BSD-2-Clause",
"engines": {
"node": ">=20.19.0"
},
"funding": {
"url": "https://github.com/fb55/entities?sponsor=1"
}
},
"node_modules/env-paths": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz",
@@ -7160,6 +7240,75 @@
"hermes-estree": "0.25.1"
}
},
"node_modules/html-dom-parser": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/html-dom-parser/-/html-dom-parser-8.0.0.tgz",
"integrity": "sha512-cWLJ8mt930VceOzVfY/J+T1ou9v3ENT0BgWqy5aEZjdFp37TYLXULjX8u0vD2rBpFAkK5IofOPl0y4Gq0QLIyA==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/remarkablemark"
}
],
"license": "MIT",
"dependencies": {
"domhandler": "6.0.1",
"htmlparser2": "12.0.0"
}
},
"node_modules/html-react-parser": {
"version": "6.1.3",
"resolved": "https://registry.npmjs.org/html-react-parser/-/html-react-parser-6.1.3.tgz",
"integrity": "sha512-CTsKtLJA23cgTbcJYT8vgHAPSqGY7NM+/mv+Tv6I0DqkLCa3FlceK2htH8AwlGa03BopqnVvK/XHqQq7HAw/sg==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/remarkablemark"
},
{
"type": "opencollective",
"url": "https://opencollective.com/html-react-parser"
}
],
"license": "MIT",
"dependencies": {
"domhandler": "6.0.1",
"html-dom-parser": "8.0.0",
"react-property": "2.0.2",
"style-to-js": "2.0.0"
},
"peerDependencies": {
"@types/react": "0.14 || 15 || 16 || 17 || 18 || 19",
"react": "0.14 || 15 || 16 || 17 || 18 || 19"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/htmlparser2": {
"version": "12.0.0",
"resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-12.0.0.tgz",
"integrity": "sha512-Tz7u1i95/g2x2jz81+x0FBVhBhY5aRTvD3tXXdFaljuNdzDLJ8UGNRrTcj2cgQvAg3iW/h77Fz15nLW0L0CrZw==",
"funding": [
"https://github.com/fb55/htmlparser2?sponsor=1",
{
"type": "github",
"url": "https://github.com/sponsors/fb55"
}
],
"license": "MIT",
"dependencies": {
"domelementtype": "^3.0.0",
"domhandler": "^6.0.0",
"domutils": "^4.0.2",
"entities": "^8.0.0"
},
"engines": {
"node": ">=20.19.0"
}
},
"node_modules/http-link-header": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/http-link-header/-/http-link-header-1.1.3.tgz",
@@ -7264,6 +7413,12 @@
"node": ">=0.8.19"
}
},
"node_modules/inline-style-parser": {
"version": "0.2.7",
"resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.2.7.tgz",
"integrity": "sha512-Nb2ctOyNR8DqQoR0OwRG95uNWIC0C1lCgf5Naz5H6Ji72KZ8OcFZLz2P5sNgwlyoJ8Yif11oMuYs5pBQa86csA==",
"license": "MIT"
},
"node_modules/internal-slot": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz",
@@ -9867,6 +10022,12 @@
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==",
"dev": true
},
"node_modules/react-property": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/react-property/-/react-property-2.0.2.tgz",
"integrity": "sha512-+PbtI3VuDV0l6CleQMsx2gtK0JZbZKbpdu5ynr+lbsuvtmgbNcS3VM0tuY2QjFNOcWxvXeHjDpy42RO+4U2rug==",
"license": "MIT"
},
"node_modules/readdirp": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-5.0.0.tgz",
@@ -10650,6 +10811,24 @@
"dev": true,
"license": "MIT"
},
"node_modules/style-to-js": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/style-to-js/-/style-to-js-2.0.0.tgz",
"integrity": "sha512-amkl/SwHF/Gb430+eOiN+XToZ6VsD2nota1kCXps4k1xagZOniEVNm8zKYm7RrGm2Q6d6hjnZdqebFIunoSJng==",
"license": "MIT",
"dependencies": {
"style-to-object": "1.0.14"
}
},
"node_modules/style-to-object": {
"version": "1.0.14",
"resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-1.0.14.tgz",
"integrity": "sha512-LIN7rULI0jBscWQYaSswptyderlarFkjQ+t79nzty8tcIAceVomEVlLzH5VP4Cmsv6MtKhs7qaAiwlcp+Mgaxw==",
"license": "MIT",
"dependencies": {
"inline-style-parser": "0.2.7"
}
},
"node_modules/styled-jsx": {
"version": "5.1.6",
"resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.6.tgz",
+1
View File
@@ -23,6 +23,7 @@
"date-fns": "^4.4.0",
"date-fns-tz": "^3.2.0",
"graphql": "^16.14.2",
"html-react-parser": "^6.1.3",
"next": "^16.2.9",
"nuqs": "^2.8.9",
"react": "19.2.7",
-4
View File
@@ -11,7 +11,6 @@ import { AccordionBlock } from "./AccordionBlock";
import { EmbedBlock } from "./EmbedBlock";
import { FactBoxBlock } from "./FactBoxBlock";
import { ScheduleBlock } from "./ScheduleBlock";
import { ButtonLinkBlock } from "./ButtonLinkBlock";
import { PageSectionBlock, PageSectionNavigationBlock } from "./PageSection";
import { ContactSectionBlock, ContactSubsectionBlock } from "./ContactSection";
import { ContactListBlock } from "./ContactListBlock";
@@ -53,9 +52,6 @@ export const Blocks = ({ blocks, pageContent }: { blocks: any, pageContent?: boo
case "ScheduleBlock":
return <ScheduleBlock key={block.id} block={block} />;
break;
case "ButtonLinkBlock":
return <ButtonLinkBlock key={block.id} block={block} />;
break;
case "PageSectionBlock":
return <PageSectionBlock key={block.id} block={block} />;
break;
@@ -1,57 +0,0 @@
import { graphql } from "@/gql";
import { type ButtonLinkBlockFragment } from "@/gql/graphql";
import Link from "next/link";
import { Icon } from "@/components/general/Icon";
export const ButtonLinkBlockFragmentDefinition = graphql(`
fragment ButtonLinkBlock on ButtonLinkBlock {
buttonText: text
buttonAnchor: anchor
buttonExternalUrl: externalUrl
buttonPage: page {
url
}
}
`);
export const ButtonLinkBlock = ({
block,
}: {
block: ButtonLinkBlockFragment;
}) => {
const anchor = block.buttonAnchor ? `#${block.buttonAnchor}` : "";
const pageUrl = block.buttonPage?.url;
const externalUrl = block.buttonExternalUrl;
let href = "#";
let isExternal = false;
if (pageUrl) {
href = `${pageUrl}${anchor}`;
} else if (externalUrl) {
href = `${externalUrl}${anchor}`;
isExternal = true;
} else if (anchor) {
href = anchor;
}
if (isExternal) {
return (
<a
href={href}
className="button"
target="_blank"
rel="noopener noreferrer"
>
<span>{block.buttonText}</span>
<Icon type="externalLink" />
</a>
);
}
return (
<Link href={href} className="button">
<span>{block.buttonText}</span>
<Icon type="arrowRight" />
</Link>
);
};
+4 -4
View File
@@ -1,5 +1,6 @@
import { graphql } from "@/gql";
import { type RichTextBlockFragment } from "@/gql/graphql";
import { RichText } from "@/components/general/RichText";
import styles from "./richTextBlock.module.scss";
const RichTextBlockFragmentDefinition = graphql(`
@@ -11,9 +12,8 @@ const RichTextBlockFragmentDefinition = graphql(`
export const RichTextBlock = ({ block }: { block: RichTextBlockFragment }) => {
return (
<div
className={styles.richTextBlock}
dangerouslySetInnerHTML={{ __html: block.value }}
></div>
<div className={styles.richTextBlock}>
<RichText content={block.value} />
</div>
);
};
@@ -92,7 +92,7 @@
font-style: italic;
}
a {
a:not(:global(.button)) {
text-decoration-thickness: .1rem;
text-decoration-color: var(--color-goldenOrange);
font-weight: 500;
@@ -131,7 +131,7 @@
max-width: var(--size-width-p);
border-collapse: collapse;
margin: 0 auto;
tr {
border-top: var(--border);
@@ -139,16 +139,16 @@
border-bottom: var(--border);
}
}
th {
text-align: left;
width: 11rem;
font-weight: 600;
}
th,
td {
padding: var(--spacing-xs) 0;
}
}
}
}
+41
View File
@@ -0,0 +1,41 @@
import Link from "next/link";
import { Icon } from "@/components/general/Icon";
/**
* A link styled as a button. Internal links use next/link with a forward arrow;
* external links open in a new tab with an external icon. isExternal is inferred
* from the href unless passed explicitly.
*/
export const ButtonLink = ({
href,
isExternal,
children,
}: {
href: string;
isExternal?: boolean;
children: React.ReactNode;
}) => {
const external =
isExternal ?? (!href.startsWith("/") && !href.startsWith("#"));
if (external) {
return (
<a
href={href}
className="button"
target="_blank"
rel="noopener noreferrer"
>
<span>{children}</span>
<Icon type="externalLink" />
</a>
);
}
return (
<Link href={href} className="button">
<span>{children}</span>
<Icon type="arrowRight" />
</Link>
);
};
+4 -4
View File
@@ -6,7 +6,7 @@ export function Icon({
type?: "doc" | "arrowUp" | "arrowRight" | "externalLink" | "search" | "tickets" | "list" | "calendar" | "filter" | "noFilter" | "email" | "phone" | "instagram" | "facebook" | "flickr" | "tiktok";
}) {
return (
<div className={styles.icon}>
<span className={styles.icon}>
{type === "doc" && (
<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
@@ -89,7 +89,7 @@ export function Icon({
)}
{!type && (
<div>
<span>
<svg
viewBox="0 0 49 49"
fill="none"
@@ -100,10 +100,10 @@ export function Icon({
fill="currentColor"
/>
</svg>
</div>
</span>
)}
</div>
</span>
);
}
+32
View File
@@ -0,0 +1,32 @@
import parse, {
domToReact,
Element,
type DOMNode,
type HTMLReactParserOptions,
} from "html-react-parser";
import { ButtonLink } from "@/components/general/ButtonLink";
/**
* Renders CMS rich text (HTML) as React, swapping inline button markers
* (`<a class="button">` from the button-link Draftail feature) for the
* ButtonLink component. Everything else renders as plain HTML.
*/
const options: HTMLReactParserOptions = {
replace: (node) => {
if (
node instanceof Element &&
node.name === "a" &&
node.attribs.class?.split(/\s+/).includes("button")
) {
return (
<ButtonLink href={node.attribs.href ?? "#"}>
{domToReact(node.children as DOMNode[], options)}
</ButtonLink>
);
}
},
};
export const RichText = ({ content }: { content: string }) => {
return <>{parse(content, options)}</>;
};
+3 -9
View File
@@ -26,7 +26,6 @@ type Documents = {
"\n query allAssociations {\n index: associationIndex {\n ... on AssociationIndex {\n ...AssociationIndex\n }\n }\n associations: pages(\n contentType: \"associations.AssociationPage\"\n limit: 1000\n ) {\n ... on AssociationPage {\n ...Association\n }\n }\n }\n": typeof types.AllAssociationsDocument,
"\n query associationBySlug($slug: String!) {\n association: page(\n contentType: \"associations.AssociationPage\"\n slug: $slug\n ) {\n ... on AssociationPage {\n ...Association\n }\n }\n }\n": typeof types.AssociationBySlugDocument,
"\n fragment AccordionBlock on AccordionBlock {\n heading\n body {\n id\n blockType\n }\n }\n": typeof types.AccordionBlockFragmentDoc,
"\n fragment ButtonLinkBlock on ButtonLinkBlock {\n buttonText: text\n buttonAnchor: anchor\n buttonExternalUrl: externalUrl\n buttonPage: page {\n url\n }\n }\n": typeof types.ButtonLinkBlockFragmentDoc,
"\n fragment ContactEntityBlock on ContactEntityBlock {\n contactEntity {\n ...ContactEntity\n }\n }\n": typeof types.ContactEntityBlockFragmentDoc,
"\n fragment ContactListBlock on ContactListBlock {\n items {\n blockType\n ... on ContactEntityBlock {\n contactEntity {\n ...ContactEntity\n }\n }\n }\n }\n": typeof types.ContactListBlockFragmentDoc,
"\n fragment ContactSectionBlock on ContactSectionBlock {\n title\n text\n blocks {\n id\n blockType\n }\n }\n": typeof types.ContactSectionBlockFragmentDoc,
@@ -61,7 +60,7 @@ type Documents = {
"\n query venueBySlug($slug: String!) {\n venue: page(contentType: \"venues.VenuePage\", slug: $slug) {\n ... on VenuePage {\n ...Venue\n }\n }\n }\n": typeof types.VenueBySlugDocument,
"\n fragment VenueRentalIndex on VenueRentalIndex {\n __typename\n title\n seoTitle\n searchDescription\n lead\n body {\n ...Blocks\n }\n }\n": typeof types.VenueRentalIndexFragmentDoc,
"\n query venueRentalIndex {\n index: venueRentalIndex {\n ... on VenueRentalIndex {\n ...VenueRentalIndex\n }\n }\n venues: pages(contentType: \"venues.VenuePage\", limit: 100) {\n ... on VenuePage {\n ...Venue\n }\n }\n }\n": typeof types.VenueRentalIndexDocument,
"\n fragment LeafBlocks on StreamFieldInterface {\n id\n blockType\n field\n ... on RichTextBlock {\n ...RichTextBlock\n }\n ... on ImageWithTextBlock {\n ...ImageWithTextBlock\n }\n ... on ImageSliderBlock {\n ...ImageSliderBlock\n }\n ... on HorizontalRuleBlock {\n ...HorizontalRuleBlock\n }\n ... on FeaturedBlock {\n ...FeaturedBlock\n }\n ... on ContactListBlock {\n ...ContactListBlock\n }\n ... on EmbedBlock {\n ...EmbedBlock\n }\n ... on FactBoxBlock {\n ...FactBoxBlock\n }\n ... on ScheduleBlock {\n ...ScheduleBlock\n }\n ... on ButtonLinkBlock {\n ...ButtonLinkBlock\n }\n }\n": typeof types.LeafBlocksFragmentDoc,
"\n fragment LeafBlocks on StreamFieldInterface {\n id\n blockType\n field\n ... on RichTextBlock {\n ...RichTextBlock\n }\n ... on ImageWithTextBlock {\n ...ImageWithTextBlock\n }\n ... on ImageSliderBlock {\n ...ImageSliderBlock\n }\n ... on HorizontalRuleBlock {\n ...HorizontalRuleBlock\n }\n ... on FeaturedBlock {\n ...FeaturedBlock\n }\n ... on ContactListBlock {\n ...ContactListBlock\n }\n ... on EmbedBlock {\n ...EmbedBlock\n }\n ... on FactBoxBlock {\n ...FactBoxBlock\n }\n ... on ScheduleBlock {\n ...ScheduleBlock\n }\n }\n": typeof types.LeafBlocksFragmentDoc,
"\n fragment OneLevelOfBlocks on StreamFieldInterface {\n ...LeafBlocks\n ... on AccordionBlock {\n ...AccordionBlock\n body {\n ...LeafBlocks\n }\n }\n ... on PageSectionBlock {\n ...PageSectionBlock\n body {\n ...LeafBlocks\n }\n }\n }\n": typeof types.OneLevelOfBlocksFragmentDoc,
"\n fragment Blocks on StreamFieldInterface {\n ...OneLevelOfBlocks\n ... on AccordionBlock {\n ...AccordionBlock\n body {\n ...OneLevelOfBlocks\n }\n }\n ... on PageSectionBlock {\n ...PageSectionBlock\n body {\n ...OneLevelOfBlocks\n }\n }\n ... on ContactSectionBlock {\n ...ContactSectionBlock\n blocks {\n ...OneLevelOfBlocks\n ... on ContactSubsectionBlock {\n ...ContactSubsectionBlock\n blocks {\n ...OneLevelOfBlocks\n }\n }\n }\n }\n }\n": typeof types.BlocksFragmentDoc,
"\n fragment Image on CustomImage {\n id\n url\n width\n height\n alt\n attribution\n }\n": typeof types.ImageFragmentDoc,
@@ -97,7 +96,6 @@ const documents: Documents = {
"\n query allAssociations {\n index: associationIndex {\n ... on AssociationIndex {\n ...AssociationIndex\n }\n }\n associations: pages(\n contentType: \"associations.AssociationPage\"\n limit: 1000\n ) {\n ... on AssociationPage {\n ...Association\n }\n }\n }\n": types.AllAssociationsDocument,
"\n query associationBySlug($slug: String!) {\n association: page(\n contentType: \"associations.AssociationPage\"\n slug: $slug\n ) {\n ... on AssociationPage {\n ...Association\n }\n }\n }\n": types.AssociationBySlugDocument,
"\n fragment AccordionBlock on AccordionBlock {\n heading\n body {\n id\n blockType\n }\n }\n": types.AccordionBlockFragmentDoc,
"\n fragment ButtonLinkBlock on ButtonLinkBlock {\n buttonText: text\n buttonAnchor: anchor\n buttonExternalUrl: externalUrl\n buttonPage: page {\n url\n }\n }\n": types.ButtonLinkBlockFragmentDoc,
"\n fragment ContactEntityBlock on ContactEntityBlock {\n contactEntity {\n ...ContactEntity\n }\n }\n": types.ContactEntityBlockFragmentDoc,
"\n fragment ContactListBlock on ContactListBlock {\n items {\n blockType\n ... on ContactEntityBlock {\n contactEntity {\n ...ContactEntity\n }\n }\n }\n }\n": types.ContactListBlockFragmentDoc,
"\n fragment ContactSectionBlock on ContactSectionBlock {\n title\n text\n blocks {\n id\n blockType\n }\n }\n": types.ContactSectionBlockFragmentDoc,
@@ -132,7 +130,7 @@ const documents: Documents = {
"\n query venueBySlug($slug: String!) {\n venue: page(contentType: \"venues.VenuePage\", slug: $slug) {\n ... on VenuePage {\n ...Venue\n }\n }\n }\n": types.VenueBySlugDocument,
"\n fragment VenueRentalIndex on VenueRentalIndex {\n __typename\n title\n seoTitle\n searchDescription\n lead\n body {\n ...Blocks\n }\n }\n": types.VenueRentalIndexFragmentDoc,
"\n query venueRentalIndex {\n index: venueRentalIndex {\n ... on VenueRentalIndex {\n ...VenueRentalIndex\n }\n }\n venues: pages(contentType: \"venues.VenuePage\", limit: 100) {\n ... on VenuePage {\n ...Venue\n }\n }\n }\n": types.VenueRentalIndexDocument,
"\n fragment LeafBlocks on StreamFieldInterface {\n id\n blockType\n field\n ... on RichTextBlock {\n ...RichTextBlock\n }\n ... on ImageWithTextBlock {\n ...ImageWithTextBlock\n }\n ... on ImageSliderBlock {\n ...ImageSliderBlock\n }\n ... on HorizontalRuleBlock {\n ...HorizontalRuleBlock\n }\n ... on FeaturedBlock {\n ...FeaturedBlock\n }\n ... on ContactListBlock {\n ...ContactListBlock\n }\n ... on EmbedBlock {\n ...EmbedBlock\n }\n ... on FactBoxBlock {\n ...FactBoxBlock\n }\n ... on ScheduleBlock {\n ...ScheduleBlock\n }\n ... on ButtonLinkBlock {\n ...ButtonLinkBlock\n }\n }\n": types.LeafBlocksFragmentDoc,
"\n fragment LeafBlocks on StreamFieldInterface {\n id\n blockType\n field\n ... on RichTextBlock {\n ...RichTextBlock\n }\n ... on ImageWithTextBlock {\n ...ImageWithTextBlock\n }\n ... on ImageSliderBlock {\n ...ImageSliderBlock\n }\n ... on HorizontalRuleBlock {\n ...HorizontalRuleBlock\n }\n ... on FeaturedBlock {\n ...FeaturedBlock\n }\n ... on ContactListBlock {\n ...ContactListBlock\n }\n ... on EmbedBlock {\n ...EmbedBlock\n }\n ... on FactBoxBlock {\n ...FactBoxBlock\n }\n ... on ScheduleBlock {\n ...ScheduleBlock\n }\n }\n": types.LeafBlocksFragmentDoc,
"\n fragment OneLevelOfBlocks on StreamFieldInterface {\n ...LeafBlocks\n ... on AccordionBlock {\n ...AccordionBlock\n body {\n ...LeafBlocks\n }\n }\n ... on PageSectionBlock {\n ...PageSectionBlock\n body {\n ...LeafBlocks\n }\n }\n }\n": types.OneLevelOfBlocksFragmentDoc,
"\n fragment Blocks on StreamFieldInterface {\n ...OneLevelOfBlocks\n ... on AccordionBlock {\n ...AccordionBlock\n body {\n ...OneLevelOfBlocks\n }\n }\n ... on PageSectionBlock {\n ...PageSectionBlock\n body {\n ...OneLevelOfBlocks\n }\n }\n ... on ContactSectionBlock {\n ...ContactSectionBlock\n blocks {\n ...OneLevelOfBlocks\n ... on ContactSubsectionBlock {\n ...ContactSubsectionBlock\n blocks {\n ...OneLevelOfBlocks\n }\n }\n }\n }\n }\n": types.BlocksFragmentDoc,
"\n fragment Image on CustomImage {\n id\n url\n width\n height\n alt\n attribution\n }\n": types.ImageFragmentDoc,
@@ -218,10 +216,6 @@ export function graphql(source: "\n query associationBySlug($slug: String!) {\n
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
export function graphql(source: "\n fragment AccordionBlock on AccordionBlock {\n heading\n body {\n id\n blockType\n }\n }\n"): (typeof documents)["\n fragment AccordionBlock on AccordionBlock {\n heading\n body {\n id\n blockType\n }\n }\n"];
/**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
export function graphql(source: "\n fragment ButtonLinkBlock on ButtonLinkBlock {\n buttonText: text\n buttonAnchor: anchor\n buttonExternalUrl: externalUrl\n buttonPage: page {\n url\n }\n }\n"): (typeof documents)["\n fragment ButtonLinkBlock on ButtonLinkBlock {\n buttonText: text\n buttonAnchor: anchor\n buttonExternalUrl: externalUrl\n buttonPage: page {\n url\n }\n }\n"];
/**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
@@ -361,7 +355,7 @@ export function graphql(source: "\n query venueRentalIndex {\n index: venueR
/**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
export function graphql(source: "\n fragment LeafBlocks on StreamFieldInterface {\n id\n blockType\n field\n ... on RichTextBlock {\n ...RichTextBlock\n }\n ... on ImageWithTextBlock {\n ...ImageWithTextBlock\n }\n ... on ImageSliderBlock {\n ...ImageSliderBlock\n }\n ... on HorizontalRuleBlock {\n ...HorizontalRuleBlock\n }\n ... on FeaturedBlock {\n ...FeaturedBlock\n }\n ... on ContactListBlock {\n ...ContactListBlock\n }\n ... on EmbedBlock {\n ...EmbedBlock\n }\n ... on FactBoxBlock {\n ...FactBoxBlock\n }\n ... on ScheduleBlock {\n ...ScheduleBlock\n }\n ... on ButtonLinkBlock {\n ...ButtonLinkBlock\n }\n }\n"): (typeof documents)["\n fragment LeafBlocks on StreamFieldInterface {\n id\n blockType\n field\n ... on RichTextBlock {\n ...RichTextBlock\n }\n ... on ImageWithTextBlock {\n ...ImageWithTextBlock\n }\n ... on ImageSliderBlock {\n ...ImageSliderBlock\n }\n ... on HorizontalRuleBlock {\n ...HorizontalRuleBlock\n }\n ... on FeaturedBlock {\n ...FeaturedBlock\n }\n ... on ContactListBlock {\n ...ContactListBlock\n }\n ... on EmbedBlock {\n ...EmbedBlock\n }\n ... on FactBoxBlock {\n ...FactBoxBlock\n }\n ... on ScheduleBlock {\n ...ScheduleBlock\n }\n ... on ButtonLinkBlock {\n ...ButtonLinkBlock\n }\n }\n"];
export function graphql(source: "\n fragment LeafBlocks on StreamFieldInterface {\n id\n blockType\n field\n ... on RichTextBlock {\n ...RichTextBlock\n }\n ... on ImageWithTextBlock {\n ...ImageWithTextBlock\n }\n ... on ImageSliderBlock {\n ...ImageSliderBlock\n }\n ... on HorizontalRuleBlock {\n ...HorizontalRuleBlock\n }\n ... on FeaturedBlock {\n ...FeaturedBlock\n }\n ... on ContactListBlock {\n ...ContactListBlock\n }\n ... on EmbedBlock {\n ...EmbedBlock\n }\n ... on FactBoxBlock {\n ...FactBoxBlock\n }\n ... on ScheduleBlock {\n ...ScheduleBlock\n }\n }\n"): (typeof documents)["\n fragment LeafBlocks on StreamFieldInterface {\n id\n blockType\n field\n ... on RichTextBlock {\n ...RichTextBlock\n }\n ... on ImageWithTextBlock {\n ...ImageWithTextBlock\n }\n ... on ImageSliderBlock {\n ...ImageSliderBlock\n }\n ... on HorizontalRuleBlock {\n ...HorizontalRuleBlock\n }\n ... on FeaturedBlock {\n ...FeaturedBlock\n }\n ... on ContactListBlock {\n ...ContactListBlock\n }\n ... on EmbedBlock {\n ...EmbedBlock\n }\n ... on FactBoxBlock {\n ...FactBoxBlock\n }\n ... on ScheduleBlock {\n ...ScheduleBlock\n }\n }\n"];
/**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
+26 -83
View File
File diff suppressed because one or more lines are too long
-3
View File
@@ -123,9 +123,6 @@ const LeafBlocksFragmentDefinition = graphql(`
... on ScheduleBlock {
...ScheduleBlock
}
... on ButtonLinkBlock {
...ButtonLinkBlock
}
}
`);