diff --git a/.github/scripts/release.sh b/.github/scripts/release.sh new file mode 100755 index 0000000000..83246814f3 --- /dev/null +++ b/.github/scripts/release.sh @@ -0,0 +1,149 @@ +#!/usr/bin/env bash + +set -e +set -u + +PRE_RELEASE=no +COMMIT=no + +while [[ $# -gt 0 ]]; do + case $1 in + --cdn) + PATH_TO_CDN=$(realpath "$2") + shift # pop option + shift # pop value + ;; + --frontend) + PATH_TO_FRONTEND=$(realpath "$2") + shift # pop option + shift # pop value + ;; + --pre-release) + PRE_RELEASE=yes + shift # pop option + ;; + --commit) + COMMIT=yes + shift # pop option + ;; + -*|--*) + echo "Unknown option $1" + exit 1 + ;; + *) + echo "Unknown argument $1" + exit 1 + ;; + esac +done + +SOURCE=src/altinn-app-frontend/dist +TARGET=toolkits/altinn-app-frontend + +if ! test -d "$PATH_TO_CDN" || ! test -d "$PATH_TO_CDN/$TARGET"; then + echo "Unable to find $PATH_TO_CDN/$TARGET" + echo "Make sure path to CDN has been passed with --cdn " + exit 1 +fi +if ! test -d "$PATH_TO_FRONTEND" || ! test -d "$PATH_TO_FRONTEND/$SOURCE"; then + echo "Unable to find $PATH_TO_FRONTEND/$SOURCE (did you run yarn build first?)" + echo "Make sure path to CDN has been passed with --frontend " + exit 1 +fi + +SOURCE_SCHEMAS="$PATH_TO_FRONTEND/schemas" +TARGET_SCHEMAS="$PATH_TO_CDN/schemas" + +SOURCE="$PATH_TO_FRONTEND/$SOURCE" +TARGET="$PATH_TO_CDN/$TARGET" + +echo "-------------------------------------" +echo "Source: $SOURCE" +echo "Target: $TARGET" +echo "Pre-release: $PRE_RELEASE (toggle with --pre-release)" +echo "Commit: $COMMIT (toggle with --commit)" +echo "-------------------------------------" + +CURRENT_VERSION=$(git describe --abbrev=0 --tags 2>/dev/null) +CURRENT_VERSION_PARTS=(${CURRENT_VERSION//./ }) +APP_FULL=${CURRENT_VERSION:1} +APP_MAJOR=${CURRENT_VERSION_PARTS[0]:1} +APP_MAJOR_MINOR=${CURRENT_VERSION_PARTS[0]:1}.${CURRENT_VERSION_PARTS[1]} +AUTHOR_FULL=$(git log -1 | grep Author) +AUTHOR_NAME=$(git log -1 | grep Author | cut -d' ' -f2) +AUTHOR_EMAIL=$(git log -1 | grep Author | cut -d' ' -f3 | cut -d'<' -f2 | cut -d'>' -f1) +COMMIT_ID=$(git rev-parse HEAD~0) + +echo "Full version: $APP_FULL" +echo "Major version: $APP_MAJOR" +echo "Major + minor: $APP_MAJOR_MINOR" +echo "Latest author: $AUTHOR_FULL" +echo "Author name: $AUTHOR_NAME" +echo "Author email: $AUTHOR_EMAIL" +echo "Commit ID: $COMMIT_ID" +echo "-------------------------------------" + +COMMIT_FILE=$(mktemp --suffix=-cdn-commit) +{ + echo "$AUTHOR_FULL updated altinn-app-frontend to $APP_FULL" + echo "based on commit https://github.com/Altinn/app-frontend-react/commit/$COMMIT_ID" + git log -1 | grep -Ev "commit|Author|Date" +} >> "$COMMIT_FILE" + +echo "CDN commit message:" +echo +cat "$COMMIT_FILE" +echo "-------------------------------------" +echo "Files to be copied:" +echo +ls -1 $SOURCE/* +echo "-------------------------------------" +echo "Log:" +echo + +# Needed in order for git commands to work +cd "$TARGET" + +if [[ "$PRE_RELEASE" == "no" ]]; then + echo " * Copying Major version" + test -e "$TARGET/$APP_MAJOR" && git rm -r "$TARGET/$APP_MAJOR" + mkdir -p "$TARGET/$APP_MAJOR" + cp -fr $SOURCE/* "$TARGET/$APP_MAJOR/" + echo " * Copying Minor version" + test -e "$TARGET/$APP_MAJOR_MINOR" && git rm -r "$TARGET/$APP_MAJOR_MINOR" + mkdir -p "$TARGET/$APP_MAJOR_MINOR" + cp -fr $SOURCE/* "$TARGET/$APP_MAJOR_MINOR/" + + echo " * Copying schemas" + cp -prv $SOURCE_SCHEMAS/* "$TARGET_SCHEMAS/" +else + echo " * Copying Major version (skipped using --pre-release)" + echo " * Copying Minor version (skipped using --pre-release)" + echo " * Copying schemas (skipped using --pre-release)" +fi + +echo " * Copying Patch version" +test -e "$TARGET/$APP_FULL" && git rm -r "$TARGET/$APP_FULL" +mkdir -p "$TARGET/$APP_FULL" +cp -fr $SOURCE/* "$TARGET/$APP_FULL/" + +echo " * Updating index.json" +ls -1 | \ + grep --perl-regexp '^[\d\.]+(-[a-z0-9.]+)?$' | \ + sort --version-sort | \ + jq --raw-input --slurp 'split("\n") | map(select(. != ""))' > index.json + +cd ../.. + +echo " * Staged for commit:" +git add . +git status --short + +if [[ "$COMMIT" == "yes" ]]; then + echo " * Committing changes" + git commit --author="$AUTHOR_NAME <$AUTHOR_EMAIL>" -F "$COMMIT_FILE" +else + echo " * Skipping commit (toggle with --commit)" +fi + +echo "-------------------------------------" diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index fbf190296c..10625a8ff4 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -36,63 +36,16 @@ jobs: token: ${{secrets.ALTINN_CDN_TOKEN}} path: cdn - - name: Prepare version number - id: prepare + - name: Run release script working-directory: app-frontend - run: | - echo Clone, copy, commit and push to Altinn-CDN - CURRENT_VERSION=`git describe --abbrev=0 --tags 2>/dev/null` - CURRENT_VERSION_PARTS=(${CURRENT_VERSION//./ }) - echo ::set-output name=APP_FULL::${CURRENT_VERSION:1} - echo ::set-output name=APP_MAJOR::${CURRENT_VERSION_PARTS[0]:1} - echo ::set-output name=APP_MAJOR_MINOR::${CURRENT_VERSION_PARTS[0]:1}.${CURRENT_VERSION_PARTS[1]} - echo ::set-output name=AUTHOR_FULL::$(git log -1 | grep Author) - echo ::set-output name=AUTHOR_NAME::$(git log -1 | grep Author | cut -d' ' -f2) - echo ::set-output name=AUTHOR_EMAIL::$(git log -1 | grep Author | cut -d' ' -f3 | cut -d'<' -f2 | cut -d'>' -f1) - echo ::set-output name=COMMIT_ID::$(git rev-parse HEAD~0) - git log -1 | grep -Ev "commit|Author|Date" > ./../commitmsg.txt - - - name: Copy version - working-directory: cdn if: "!github.event.release.prerelease" - run: | - echo altinn-app-frontend version: ${{ steps.prepare.outputs.APP_FULL }} - echo Copy Major Version - test -e toolkits/altinn-app-frontend/${{ steps.prepare.outputs.APP_MAJOR }} && git rm -r toolkits/altinn-app-frontend/${{ steps.prepare.outputs.APP_MAJOR }} - mkdir -p toolkits/altinn-app-frontend/${{ steps.prepare.outputs.APP_MAJOR }} - cp -fr ../app-frontend/src/altinn-app-frontend/dist/* toolkits/altinn-app-frontend/${{ steps.prepare.outputs.APP_MAJOR }}/ - echo Copy Minor Version - test -e toolkits/altinn-app-frontend/${{ steps.prepare.outputs.APP_MAJOR_MINOR }} && git rm -r toolkits/altinn-app-frontend/${{ steps.prepare.outputs.APP_MAJOR_MINOR }} - mkdir -p toolkits/altinn-app-frontend/${{ steps.prepare.outputs.APP_MAJOR_MINOR }} - cp -fr ../app-frontend/src/altinn-app-frontend/dist/* toolkits/altinn-app-frontend/${{ steps.prepare.outputs.APP_MAJOR_MINOR }}/ - echo Copy Patch - test -e toolkits/altinn-app-frontend/${{ steps.prepare.outputs.APP_FULL }} && git rm -r toolkits/altinn-app-frontend/${{ steps.prepare.outputs.APP_FULL }} - mkdir -p toolkits/altinn-app-frontend/${{ steps.prepare.outputs.APP_FULL }} - cp -fr ../app-frontend/src/altinn-app-frontend/dist/* toolkits/altinn-app-frontend/${{ steps.prepare.outputs.APP_FULL }}/ + run: bash .github/scripts/release.sh --frontend . --cdn ../cdn --commit - - name: Copy version (pre-release) - working-directory: cdn + - name: Run release script (pre-release) + working-directory: app-frontend if: "github.event.release.prerelease" - run: | - echo altinn-app-frontend version: ${{ steps.prepare.outputs.APP_FULL }} - echo Copy Patch - test -e toolkits/altinn-app-frontend/${{ steps.prepare.outputs.APP_FULL }} && git rm -r toolkits/altinn-app-frontend/${{ steps.prepare.outputs.APP_FULL }} - mkdir -p toolkits/altinn-app-frontend/${{ steps.prepare.outputs.APP_FULL }} - cp -fr ../app-frontend/src/altinn-app-frontend/dist/* toolkits/altinn-app-frontend/${{ steps.prepare.outputs.APP_FULL }}/ + run: bash .github/scripts/release.sh --frontend . --cdn ../cdn --commit --pre-release - - name: Copy patch version and commit to CDN + - name: Push to CDN working-directory: cdn - run: | - echo altinn-app-frontend version: ${{ steps.prepare.outputs.APP_FULL }} - cd toolkits/altinn-app-frontend - ls -1 | grep --perl-regexp '^[\d\.]+(-[a-z0-9.]+)?$' | sort --version-sort | jq --raw-input --slurp 'split("\n") | map(select(. != ""))' > index.json - cd ../.. - git config --global user.email "${{ steps.prepare.outputs.AUTHOR_EMAIL }}" - git config --global user.name "${{ steps.prepare.outputs.AUTHOR_NAME }}" - git add . - echo "${{ steps.prepare.outputs.AUTHOR_FULL }} updated altinn-app-frontend to ${{ steps.prepare.outputs.APP_FULL }}" > ./../commit1.txt - echo "based on commit https://github.com/Altinn/app-frontend-react/commit/${{ steps.prepare.outputs.COMMIT_ID }}" > ./../commit2.txt - cat ./../commit1.txt ./../commit2.txt ./../commitmsg.txt > ./../commit.txt - cat ./../commit.txt - git commit -F ./../commit.txt - git push + run: git push diff --git a/schemas/json/component/number-format.schema.v1.json b/schemas/json/component/number-format.schema.v1.json new file mode 100644 index 0000000000..5c2c6768d6 --- /dev/null +++ b/schemas/json/component/number-format.schema.v1.json @@ -0,0 +1,99 @@ +{ + "$id": "https://altinncdn.no/schemas/json/component/number-format.schema.v1.json", + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Input number formatting", + "description": "Schema that describes the options that can be configured for number formatting on an `input` component, based on react-number-format package. For complete list of available options, see https://github.com/s-yadav/react-number-format#props", + "type": "object", + "additionalProperties": true, + "properties": { + "allowedDecimalSeparators": { + "title": "Allowed decimal separators", + "description": "Characters which when pressed result in a decimal separator. When missing, decimalSeparator and '.' are used", + "type": "array", + "items": { + "type": "string", + "maxLength": 1 + }, + "examples": [[",", ".", "/"]] + }, + "allowEmptyFormatting": { + "title": "Allow empty formatting", + "description": "Apply formatting to empty inputs", + "type": "boolean", + "default": false + }, + "allowLeadingZeros": { + "title": "Allow leading zeros", + "description": "Allow leading zeros at beginning of number", + "type": "boolean", + "default": false + }, + "allowNegative": { + "title": "Allow negative", + "description": "Allow negative numbers (Only when format option is not provided)", + "type": "boolean", + "default": true + }, + "decimalScale": { + "title": "Decimal scale", + "description": "If defined it limits to given decimal scale.", + "type": "number", + "examples": [1, 2, 3] + }, + "decimalSeparator": { + "title": "Decimal separator", + "description": "Support decimal point on a number. Single character string.", + "type": "string", + "maxLength": 1, + "default": "." + }, + "fixedDecimalScale": { + "title": "Fixed decimal scale", + "description": "Used together with decimalScale. If true it adds 0s to match given decimal scale.", + "type": "boolean", + "default": false + }, + "format": { + "title": "Format", + "description": "Format given as hash string, to allow number input in place of hash.", + "type": "string", + "examples": ["### ### ###", "+47 ### ## ###", "##-##-##-##"] + }, + "mask": { + "title": "Mask", + "description": "Mask to show in place of non-entered values", + "type": "string", + "examples": ["_"], + "default": " " + }, + "prefix": { + "title": "Prefix", + "description": "Add a prefix before the number", + "type": "string", + "examples": ["$", "kr", "-", "(+47) "] + }, + "suffix": { + "title": "Suffix", + "description": "Add a suffix after the number", + "type": "string", + "examples": ["%", "kr", "kg"] + }, + "thousandSeparator": { + "title": "Thousand separator", + "description": "Add thousand separators on number. Single character string or boolean true (true is default to ,)", + "type": ["string", "boolean"], + "maxLength": 1, + "examples": [true, ",", "."] + } + }, + "allOf": [{ + "if": { + "properties": { "fixedDecimalScale": { "const": true } }, + "required": ["fixedDecimalScale"] + }, + "then": { + "required": ["decimalScale"] + } + } + ] +} diff --git a/schemas/json/layout/layout-sets.schema.v1.json b/schemas/json/layout/layout-sets.schema.v1.json new file mode 100644 index 0000000000..8267bbf306 --- /dev/null +++ b/schemas/json/layout/layout-sets.schema.v1.json @@ -0,0 +1,47 @@ +{ + "$id": "https://altinncdn.no/schemas/json/layout/layout-sets.schema.v1.json", + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Altinn layout sets", + "description": "Schema that describes the different layout sets for an Altinn application and when to use them", + "type": "object", + "additionalProperties": true, + "properties": { + "sets": { + "type": "array", + "items": { + "$ref": "#/definitions/layoutset" + } + }, + "definitions": { + "layoutset": { + "type": "object", + "additionalProperties": false, + "description": "Settings regarding a specific layoutset", + "properties": { + "id": { + "type": "string", + "title": "id", + "description": "The layoutset ID. Must be unique within a given application." + }, + "type": { + "type": "string", + "title": "dataType", + "description": "The datatype to use this layoyut." + }, + "tasks": { + "$ref": "#/definitions/tasks" + } + } + }, + "tasks": { + "additionalProperties": false, + "description": "An array specifying which task to use a layoutset", + "type": "array", + "items": { + "description": "A layoutSet name, for instance 'Form1'", + "type": "string" + } + } + } + } +} diff --git a/schemas/json/layout/layout.schema.v1.json b/schemas/json/layout/layout.schema.v1.json new file mode 100644 index 0000000000..23932cd27b --- /dev/null +++ b/schemas/json/layout/layout.schema.v1.json @@ -0,0 +1,735 @@ +{ + "$id": "https://altinncdn.no/schemas/json/layout/layout.schema.v1.json", + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Altinn layout", + "description": "Schema that describes the layout configuration for Altinn applications.", + "type": "object", + "additionalProperties": true, + "properties": { + "data": { + "$ref": "#/definitions/data" + } + }, + "definitions": { + "data": { + "title": "The layout data", + "description": "Contains data describing the layout configuration.", + "type": "object", + "properties": { + "layout": { + "$ref": "#/definitions/layout" + } + } + }, + "layout": { + "title": "The layout", + "description": "Array of components to be presented in the layout.", + "type": "array", + "items": { + "$ref": "#/definitions/component" + } + }, + "component": { + "type": "object", + "properties": { + "id": { + "type": "string", + "title": "id", + "pattern": "^[0-9a-zA-Z][0-9a-zA-Z-]*[0-9a-zA-Z]$", + "description": "The component ID. Must be unique within a given layout." + }, + "type": { + "type": "string", + "title": "Type", + "description": "The component type.", + "enum": ["AddressComponent", "AttachmentList", "Button", "Checkboxes", "Datepicker", "Dropdown", "FileUpload", "FileUploadWithTag", "Group", "Header", "Image", "Input", "InstantiationButton", "Likert", "MultipleSelect", "NavigationButtons", "NavigationBar", "Panel", "Paragraph", "PrintButton", "RadioButtons", "Summary", "TextArea"] + }, + "required": { + "type": "boolean", + "title": "Required", + "description": "Boolean value indicating if the component is required when filling in the form. Defaults to false.", + "default": false + }, + "readOnly": { + "type": "boolean", + "title": "Read only", + "description": "Boolean value indicating if the component should be presented as read only. Defaults to false.", + "default": false + }, + "textResourceBindings": { + "type": "object", + "title": "Text resource bindings", + "description": "Text resource bindings for a component.", + "additionalProperties": { + "type": "string" + }, + "examples": [ + { + "title": "some.text.binding", + "help": "some.other.text.binding" + } + ] + }, + "dataModelBindings": { + "type": "object", + "title": "Data model bindings", + "description": "Data model bindings for a component.", + "additionalProperties": { + "type": "string" + }, + "examples": [ + { + "simpleBinding": "some.data.binding" + } + ] + }, + "triggers": { + "$ref": "#/definitions/triggers" + }, + "labelSettings": { + "type": "object", + "title": "Label settings", + "description": "A collection of settings for how the component label should be rendered.", + "properties": { + "optionalIndicator": { + "type": "boolean", + "title": "Optional indicator", + "description": "Controls whether the text that is indicating that a field is optional should be displayed.", + "default": true + } + } + }, + "grid": { + "type": "object", + "title": "Grid", + "description": "Settings for the components grid. Used for controlling horizontal alignment.", + "$ref": "#/definitions/gridSettings", + "examples": [ + { + "xs": 12 + } + ] + } + }, + "required": ["id", "type"], + "allOf": [ + { "if": {"properties": {"type": { "const": "AddressComponent"}}}, "then": { "$ref": "#/definitions/addressComponent"}}, + { "if": {"properties": {"type": { "const": "AttachmentList"}}}, "then": { "$ref": "#/definitions/attachmentListComponent"}}, + { "if": {"properties": {"type": { "const": "Checkboxes"}}}, "then": { "$ref": "#/definitions/radioAndCheckboxComponents"}}, + { "if": {"properties": {"type": { "const": "Datepicker"}}}, "then": { "$ref": "#/definitions/datepickerComponent"}}, + { "if": {"properties": {"type": { "const": "Dropdown"}}}, "then": { "$ref": "#/definitions/selectionComponents"}}, + { "if": {"properties": {"type": { "const": "FileUpload"}}}, "then": { "$ref": "#/definitions/fileUploadComponent"}}, + { "if": {"properties": {"type": { "const": "FileUploadWithTag"}}}, "then": { "$ref": "#/definitions/fileUploadWithTagComponent"}}, + { "if": {"properties": {"type": { "const": "Group"}}}, "then": { "$ref": "#/definitions/groupComponent"}}, + { "if": {"properties": {"type": { "const": "Image"}}}, "then": { "$ref": "#/definitions/imageComponent"}}, + { "if": {"properties": {"type": { "const": "Input"}}}, "then": { "$ref": "#/definitions/inputComponent"}}, + { "if": {"properties": {"type": { "const": "TextArea"}}}, "then": { "$ref": "#/definitions/textAreaComponent"}}, + { "if": {"properties": {"type": { "const": "InstantiationButton"}}}, "then": { "$ref": "#/definitions/instantiationButtonComponent"}}, + { "if": {"properties": {"type": { "const": "Likert"}}}, "then": { "$ref": "#/definitions/radioAndCheckboxComponents"}}, + { "if": {"properties": {"type": { "const": "MultipleSelect"}}}, "then": { "$ref": "#/definitions/selectionComponents"}}, + { "if": {"properties": {"type": { "const": "NavigationButtons"}}}, "then": { "$ref": "#/definitions/navigationButtonsComponent"}}, + { "if": {"properties": {"type": { "const": "RadioButtons"}}}, "then": { "$ref": "#/definitions/radioAndCheckboxComponents"}}, + { "if": {"properties": {"type": { "const": "Summary"}}}, "then": {"$ref": "#/definitions/summaryComponent"}}, + { "if": {"properties": {"type": { "const": "Header"}}}, "then": {"$ref": "#/definitions/headerComponent"}}, + { "if": {"properties": {"type": { "const": "Panel"}}}, "then": {"$ref": "#/definitions/panelComponent"}} + ] + }, + "headerComponent": { + "properties": { + "size": { + "title": "Header size", + "description": "'L'=

, 'M'=

, 'S'=

", + "type": "string", + "enum": ["L", "M", "S", "h2", "h3", "h4"] + } + }, + "required": ["size"] + }, + "panelComponent": { + "properties": { + "variant": { + "title": "Panel variant", + "description": "Change the look of the panel.", + "type": "string", + "enum": ["info", "warning", "success"], + "default": "info" + }, + "showIcon": { + "title": "Show icon", + "description": "Boolean value indicating if the icon should be shown.", + "type": "boolean", + "default": true + } + } + }, + "fileUploadComponent": { + "properties": { + "maxFileSizeInMB": { + "title": "Maximum file size in MB", + "description": "Sets the maximum file size allowed in megabytes.", + "type": "integer", + "minimum": 0 + }, + "maxNumberOfAttachments": { + "title": "Maximum allowed attachments", + "description": "Sets the maximum number of attachments allowed to upload.", + "type": "integer", + "minimum": 0 + }, + "minNumberOfAttachments": { + "title": "Minimum allowed attachments", + "description": "Sets the minimum number of attachments to upload", + "type": "integer", + "minimum": 0 + }, + "displayMode": { + "title": "Display mode", + "description": "Sets the display mode for the file upload component.", + "type": "string", + "enum": ["simple", "list"] + }, + "hasCustomFileEndings": { + "title": "Has custom file endings", + "description": "Boolean value indicating if the component has valid file endings", + "type": "boolean" + }, + "validFileEndings": { + "title": "Valid file endings", + "description": "A separated string of valid file endings to upload. If not set all endings are accepted.", + "examples": [".csv", ".doc", ".docx", ".gif", ".jpeg", ".pdf", ".txt"], + "anyOf": [ + { + "type": "string" + }, + { + "type": "array", + "items": { + "type": "string" + } + } + ] + } + }, + "required": ["displayMode", "maxFileSizeInMB", "maxNumberOfAttachments", "minNumberOfAttachments" ] + }, + "fileUploadWithTagComponent": { + "allOf": [ + { "$ref": "#/definitions/fileUploadComponent" } + ], + "properties": { + "optionsId": { + "type": "string", + "title": "Options ID", + "description": "Reference to connected options by id." + }, + "mapping": { + "$ref": "#/definitions/mapping", + "description": "Optionally used to map options" + } + }, + "required": ["optionsId"] + }, + "datepickerComponent": { + "properties": { + "minDate": { + "type": "string", + "title": "Minimum allowed date", + "description": "Sets the minimum allowed date. Can also use keyword 'today' to disable all past dates dynamically based on the current date. Defaults to 1900-01-01T12:00:00.000Z.", + "default": "1900-01-01T12:00:00.000Z" + }, + "maxDate": { + "type": "string", + "title": "Maximum allowed date", + "description": "Sets the maximum allowed date. Can also use keyword 'today' to disable all future dates dynamically based on the current date. Defaults to 2100-01-01T12:00:00.000Z.", + "default": "2100-01-01T12:00:00.000Z." + }, + "timeStamp": { + "type": "boolean", + "title": "Time stamp", + "description": "Boolean value indicating if the date time should be stored as a timeStamp. Defaults to true.", + "default": true + }, + "format": { + "type": "string", + "title": "Date format", + "description": "Long date format used when displaying the date to the user. The user date format from the locale will be prioritized over this setting.", + "examples": ["DD/MM/YYYY", "MM/DD/YYYY", "YYYY-MM-DD"], + "default": "DD.MM.YYYY" + } + }, + "required": [] + }, + "navigationButtonsComponent": { + "properties": { + "showBackButton": { + "type": "boolean", + "title": "Show back button", + "description": "Shows two buttons (back/next) instead of just 'next'." + } + } + }, + "instantiationButtonComponent": { + "properties": { + "mapping": { + "$ref": "#/definitions/mapping", + "description": "Creates a new app instance with data collected from a stateless part of the app." + } + } + }, + "gridValue": { + "type": "integer", + "maximum": 12, + "minimum": 1, + "examples": [ + 12 + ] + }, + "gridSettings": { + "allOf": [ + { + "$ref": "#/definitions/gridProps" + } + ], + "properties": { + "labelGrid": { + "title": "labelGrid", + "description": "Optional grid for the component label. Used in combination with innerGrid to align labels on the side.", + "examples": [ + { + "xs": 12 + } + ], + "$ref": "#/definitions/gridProps" + }, + "innerGrid": { + "title": "innerGrid", + "description": "Optional grid for inner component content like input field or dropdown. Used to avoid inner content filling the component width.", + "examples": [ + { + "xs": 12 + } + ], + "$ref": "#/definitions/gridProps" + } + } + }, + "gridProps": { + "properties": { + "xs": { + "$ref": "#/definitions/gridValue", + "title": "xs", + "description": "Grid breakpoint at 0px" + }, + "sm": { + "$ref": "#/definitions/gridValue", + "title": "sm", + "description": "Grid breakpoint at 600px" + }, + "md": { + "$ref": "#/definitions/gridValue", + "title": "md", + "description": "Grid breakpoint at 960px" + }, + "lg": { + "$ref": "#/definitions/gridValue", + "title": "lg", + "description": "Grid breakpoint at 1280px" + }, + "xl": { + "$ref": "#/definitions/gridValue", + "title": "xl", + "description": "Grid breakpoint at 1920px" + } + } + }, + "groupComponent": { + "properties": { + "children": { + "title": "Children", + "description": "An array of the \"id\" of child components belonging to the group.", + "type": "array", + "items": { + "type": "string" + }, + "uniqueItems": true + }, + "edit": { + "title": "Edit", + "description": "Alternatives for edit view of repeating group", + "$ref": "#/definitions/groupEditOptions" + }, + "panel": { + "title": "Panel", + "description": "Alternatives for panel view of repeating group", + "$ref": "#/definitions/groupPanelOptions" + }, + "maxCount": { + "type": "integer", + "title": "Maximum count", + "description": "The maximum number of iterations of a group. Only relevant if group is repeating.", + "minimum": 0 + }, + "tableHeaders": { + "title": "Table Headers", + "description": "An array of the id of child components that should be included as table headers. If not defined all components are shown.", + "type": "array", + "items": { + "type": "string" + }, + "uniqueItems": true + } + }, + "required": ["children"] + }, + "groupEditOptions": { + "properties": { + "mode": { + "title": "Edit mode", + "description": "Mode for how repeating group table is displayed in edit mode", + "type": "string", + "enum": ["hideTable", "likert", "showAll", "showTable"] + }, + "filter": { + "title": "Filter", + "description": "Conditions for filtering visible items in repeating group", + "type": "array", + "items": { + "$ref": "#/definitions/groupFilterItem" + } + }, + "saveButton": { + "title": "Save button", + "description": "Boolean value indicating whether save button should be shown or not in edit mode of repeating group item.", + "type": "boolean" + }, + "deleteButton": { + "title": "Delete button", + "description": "Boolean value indicating whether delete button should be shown or not in edit mode of repeating group item.", + "type": "boolean" + }, + "multiPage": { + "title": "Multi-page", + "description": "Boolean value indicating if form components in edit mode should be shown over multiple pages/views.", + "type": "boolean" + }, + "addButton": { + "title": "Add button", + "description": "Boolean value indicating whether add new button should be shown or not under the table.", + "type": "boolean" + }, + "openByDefault": { + "title": "Open by default", + "description": "Boolean value indicating if group should be opened to add a new item by default when no items exist.", + "type": "boolean" + }, + "alertOnDelete": { + "title": "Alert on delete", + "description": "Boolean value indicating if warning popup should be displayed when attempting to delete a row", + "type": "boolean" + } + } + }, + "groupPanelOptions": { + "additionalProperties": false, + "allOf": [{ + "$ref": "#/definitions/panelComponent" + }], + "properties": { + "iconUrl": { + "title": "Icon url", + "description": "Url of the icon to be shown in panel. Can be relative if hosted by app or full if referencing a cdn or other hosting.", + "type": "string", + "examples": ["fancyIcon.svg", "https://cdn.example.com/fancyIcon.svg"] + }, + "iconAlt": { + "title": "Icon alt", + "description": "Alternative text for the icon. Only applicable if iconUrl is provided. Can be plain text or a text resource reference.", + "type": "string" + }, + "groupReference": { + "title": "Group reference", + "description": "Reference to the group that is being displayed in the panel. Used for referencing another repeating group context.", + "type": "object", + "properties": { + "group" : { + "type": "string", + "title": "Group", + "description": "Group reference. Can be either the group id or the group data model binding.", + "examples": ["the-group-id", "some.model.theGroup"] + } + } + } + } + }, + "groupFilterItem": { + "properties": { + "key": { + "title": "Key", + "description": "Key representing field in data model to check.", + "type": "string" + }, + "value": { + "title": "Value", + "description": "Value to check against.", + "type": "string" + } + } + }, + "options": { + "properties": { + "label": { + "type": "string", + "title": "Label", + "description": "The option label. Can be plain text or a text resource binding." + }, + "value": { + "type": "string", + "title": "Value", + "description": "The option value." + } + }, + "required": ["label", "value"] + }, + "triggers": { + "title": "Triggers", + "description": "An array of actions that should be triggered when data connected to this component changes.", + "type": "array", + "items": { + "type": "string", + "enum": ["validation", "validateRow", "validatePage", "validateAllPages", "calculatePageOrder"] + } + }, + "selectionComponents": { + "properties": { + "optionsId": { + "type": "string", + "title": "Options ID", + "description": "Reference to connected options by id." + }, + "options": { + "type": "array", + "title": "Options", + "description": "An array of options. Only relevant if no optionsId is set.", + "items": { + "$ref": "#/definitions/options" + } + }, + "preselectedOptionIndex": { + "type": "integer", + "title": "Preselected option index", + "description": "Sets a preselected index.", + "minimum": 0 + }, + "secure": { + "type": "boolean", + "title": "Secure Options", + "description": "Boolean value indicating if the options should be instance aware. Defaults to false. See more on docs: https://docs.altinn.studio/app/development/data/options/" + }, + "source": { + "type": "object", + "title": "Source", + "description": "Object to define a data model source to be used as basis for options. Can not be used if options or optionId is set. See more on docs: https://docs.altinn.studio/app/development/data/options/", + "properties": { + "group": { + "type": "string", + "title": "Group", + "description": "The repeating group to base options on.", + "examples": ["model.some.group"] + }, + "label": { + "type": "string", + "title": "Label", + "description": "Reference to a text resource to be used as the option label.", + "examples": ["some.text.key"] + }, + "value": { + "type": "string", + "title": "Label", + "description": "Field in the group that should be used as value", + "examples": ["model.some.group[{0}].someField"] + } + }, + "required": ["group", "label", "value"] + }, + "mapping": { + "$ref": "#/definitions/mapping", + "description": "Optionally used to map options" + } + } + }, + "radioAndCheckboxComponents": { + "allOf": [{ "$ref": "#/definitions/selectionComponents" }], + "properties": { + "layout": { + "type": "string", + "enum": ["column", "row", "table"], + "title": "Layout", + "description": "Define the layout style for the options" + } + } + }, + "addressComponent": { + "properties": { + "simplified": { + "type": "boolean", + "title": "Simplified", + "description": "Boolean value indicating if the address component should be shown in simple mode.", + "default": false + }, + "saveWhileTyping": { + "$ref": "#/definitions/saveWhileTyping" + } + } + }, + "summaryComponent": { + "properties": { + "componentRef": { + "type": "string", + "title": "Component reference", + "description": "String value indicating which layout component (by ID) the summary is for." + }, + "pageRef": { + "type": "string", + "title": "Page reference", + "description": "String value indicating which layout page the referenced component is defined on." + }, + "largeGroup": { + "type": "boolean", + "title": "Large group", + "description": "Boolean value indicating if summary of repeating group should be displayed in large format. Useful for displaying summary with nested groups." + }, + "display": { + "type": "object", + "title": "Display properties", + "description": "Optional properties to configure how summary is displayed", + "properties": { + "hideChangeButton": { + "type": "boolean", + "title": "Hide change button", + "description": "Set to true if the change button should be hidden for the summary component. False by default." + }, + "hideBottomBorder": { + "type": "boolean", + "title": "Hide bottom border", + "description": "Set to true to hide the blue dashed border below the summary component. False by default." + }, + "useComponentGrid": { + "type": "boolean", + "title": "Use component grid", + "description": "Set to true to allow summary component to use the grid setup of the referenced component. For group summary, this will apply for all group child components." + } + } + } + } + }, + "attachmentListComponent": { + "properties": { + "dataTypeIds": { + "type": "array", + "items": { + "type": "string" + }, + "title": "Data type IDs", + "description": "List of data type IDs for the attachment list to show.", + "examples": [["SomeDataType", "SomeOtherDataType"]] + } + } + }, + "imageComponent": { + "properties": { + "image": { + "type": "object", + "title": "Image properties", + "description": "Set of options for image field.", + "properties": { + "src": { + "title": "Image source", + "description": "", + "type": "object", + "properties": { + "nb" :{ + "type": "string", + "title": "Bokmål" + }, + "nn":{ + "type": "string", + "title": "Nynorsk" + }, + "en":{ + "type": "string", + "title": "English" + } + }, + "additionalProperties": true + }, + "width": { + "type":"string", + "title": "Image width", + "examples": ["100%"] + }, + "align": { + "type":"string", + "title": "Align image", + "enum": ["flex-start", "center", "flex-end", "space-between", "space-around", "space-evenly"] + } + }, + "required": ["src", "width", "align"] + } + } + }, + "inputComponent": { + "properties": { + "formatting": { + "title": "Input formatting", + "description": "Set of options for formatting input fields.", + "$ref": "#/definitions/inputFormatting" + }, + "saveWhileTyping": { + "$ref": "#/definitions/saveWhileTyping" + } + } + }, + "textAreaComponent": { + "properties": { + "saveWhileTyping": { + "$ref": "#/definitions/saveWhileTyping" + } + } + }, + "saveWhileTyping": { + "title": "Automatic saving while typing", + "description": "Boolean or number. True = feature on (default), false = feature off (saves on focus blur), number = timeout in milliseconds (400 by default)", + "default": true, + "oneOf": [ + { "type": "boolean" }, + { "type": "number" } + ] + }, + "inputFormatting": { + "properties": { + "number": { + "$ref": "https://altinncdn.no/schemas/json/component/number-format.schema.v1.json" + }, + "align": { + "type": "string", + "title": "Align input", + "description": "The alignment for Input field (eg. right aligning a series of numbers)", + "enum": [ "left", "center", "right"] + } + } + }, + "mapping": { + "type": "object", + "title": "Mapping", + "examples": [ + { + "some.source.field": "key1", + "some.other.source": "key2" + } + ], + "additionalProperties": { + "type": "string" + } + } + } +} diff --git a/schemas/json/layout/layoutSettings.schema.v1.json b/schemas/json/layout/layoutSettings.schema.v1.json new file mode 100644 index 0000000000..66929b7f88 --- /dev/null +++ b/schemas/json/layout/layoutSettings.schema.v1.json @@ -0,0 +1,82 @@ +{ + "$id": "https://altinncdn.no/schemas/json/layout/layoutSettings.schema.v1.json", + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Altinn layout settings", + "description": "Schema that describes settings for the layout configuration for Altinn applications.", + "type": "object", + "additionalProperties": true, + "properties": { + "components": { + "type": "object", + "$ref": "#/definitions/components" + }, + "pages": { + "type": "object", + "$ref": "#/definitions/pages" + } + }, + "definitions": { + "components": { + "type": "object", + "additionalProperties": false, + "description": "Settings regarding components", + "properties": { + "excludeFromPdf": { + "$ref": "#/definitions/excludeFromPdf" + } + } + }, + "pages": { + "type": "object", + "additionalProperties": false, + "description": "Settings regarding layout pages", + "properties": { + "order": { + "$ref": "#/definitions/order" + }, + "excludeFromPdf": { + "$ref": "#/definitions/excludeFromPdf" + }, + "triggers": { + "$ref": "https://altinncdn.no/schemas/json/layout/layout.schema.v1.json#/definitions/triggers", + "title": "Triggers", + "description": "Triggers that apply for all navigation components across all pages. Can be overrided at the component level." + }, + "hideCloseButton": { + "$ref": "#/definitions/hideCloseButton" + }, + "showLanguageSelector": { + "$ref": "#/definitions/showLanguageSelector" + } + } + }, + "order": { + "additionalProperties": false, + "description": "An array specifying which order the pages should appear in the application", + "type": "array", + "items": { + "description": "A layout name, for instance 'Page1'", + "type": "string" + } + }, + "excludeFromPdf": { + "additionalProperties": false, + "description": "An array specifying which pages to exclude from the pdf", + "type": "array", + "items": { + "description": "A layout name, for instance 'Page1'", + "type": "string" + } + }, + "hideCloseButton": { + "additionalProperties": false, + "description": "An attribute specifiying if the close button should be hidden", + "type": "boolean" + }, + "showLanguageSelector": { + "additionalProperties": false, + "description": "An attribute specifying if the translation dropdown menu should be visible", + "type": "boolean" + } + } +} \ No newline at end of file