Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Form builder #915

Merged
merged 48 commits into from
Jan 14, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
55903d1
Export id as prop for Checkbox and TextInput
pavish Dec 2, 2021
9a8cd28
Create DynamicInput component
pavish Dec 2, 2021
f0e8245
Run npm audit for production on workflow
pavish Dec 2, 2021
5db1cf4
Create basic FormInput component
pavish Dec 2, 2021
b2d968a
Create Form component
pavish Dec 2, 2021
b9a0fc2
Export component with initial formFactory
pavish Dec 2, 2021
9024566
Initial story to demonstrate form generation
pavish Dec 2, 2021
c5a259a
Merge branch 'master' of https://github.com/centerofci/mathesar into …
pavish Dec 18, 2021
bd7816e
Initial implemenation of boolean, string and enum inputs
pavish Dec 20, 2021
029a5f4
Restructure DynamicInput to get value based on data type instead of d…
pavish Dec 20, 2021
f1c878b
Merge branch 'master' of https://github.com/centerofci/mathesar into …
pavish Dec 20, 2021
33f5344
Value selection for EnumInput
pavish Dec 20, 2021
d38c5f6
Defining props for dynamic input in storybook
pavish Dec 20, 2021
1a1eacf
InputGroup and InputGroupText components
pavish Dec 20, 2021
840c704
Only have input within TextInput, use InputGroup for appending and pr…
pavish Dec 20, 2021
e9ab899
InputGroup stories and tests
pavish Dec 20, 2021
970c082
NumberInput component
pavish Dec 21, 2021
0c2db70
Organize component library styles
pavish Dec 21, 2021
69b8609
Import global styles within storybook
pavish Dec 21, 2021
777b15c
Share styles between TextInput, NumberInput and TextArea
pavish Dec 21, 2021
07055a6
Support number types in DynamicInput
pavish Dec 21, 2021
f68efe7
Add basic type definitions for form elements
pavish Dec 21, 2021
761f043
FormInput and FormLayout components
pavish Dec 21, 2021
df1445e
Conditional form components - If and Switch
pavish Dec 21, 2021
6a868e6
FormElement component
pavish Dec 21, 2021
4a06fe7
Stories for Form system
pavish Dec 21, 2021
0bffb94
Merge branch 'master' of https://github.com/centerofci/mathesar into …
pavish Dec 27, 2021
42c00e0
Styling updates for new components from merge
pavish Dec 28, 2021
e305350
Create BaseInput headless component
pavish Dec 29, 2021
32a9c3b
Using BaseInput headless component in all input components
pavish Dec 29, 2021
6a942e1
Rename common style components directory, styling updates to input co…
pavish Dec 29, 2021
b7c2c60
Use LabeledInput in DynamicInput component
pavish Dec 29, 2021
69fb318
Fix label id and styling for Select component
pavish Dec 29, 2021
208a358
Form horizontal layout support
pavish Dec 29, 2021
c82150c
Better typing for form builder
pavish Dec 31, 2021
1b5d3bb
Merge branch 'master' into form-builder
pavish Jan 2, 2022
c215f6d
Merge branch 'master' of https://github.com/centerofci/mathesar into …
pavish Jan 3, 2022
b8cedee
Merge branch 'form-builder' of https://github.com/centerofci/mathesar…
pavish Jan 3, 2022
fe4dd52
Merge branch 'master' into form-builder
pavish Jan 5, 2022
4914e28
Merge branch 'master' into form-builder
pavish Jan 6, 2022
c2f7e48
Merge branch 'master' into form-builder
pavish Jan 6, 2022
a03168e
Merge branch 'master' into form-builder
pavish Jan 10, 2022
0a30de5
Fix forced resolutions in package-lock.json
pavish Jan 11, 2022
bc19fa5
Improve naming of properties in form-builder
pavish Jan 11, 2022
2ceb99b
Merge branch 'master' of https://github.com/centerofci/mathesar into …
pavish Jan 11, 2022
6d5c9e5
Fix forced resolutions in package-lock.json
pavish Jan 13, 2022
51ca4dd
Rename type to dataType and inputType to interfaceType in DynamicInput
pavish Jan 13, 2022
53a8e39
Merge branch 'master' into form-builder
seancolsen Jan 14, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/run-lint-audit-tests-ui.yml
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ jobs:
dedupe_issues: true
working_directory: './mathesar_ui'
issue_labels: 'restricted: maintainers,type: bug,work: frontend,status: triage'
production_flag: true
continue-on-error: true

tests:
Expand Down
10 changes: 10 additions & 0 deletions mathesar_ui/.storybook/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,16 @@ module.exports = {
svelte: path.resolve('node_modules', 'svelte')
}
config.resolve.extensions.push(".ts", ".tsx", ".mjs", ".js", ".svelte");
config.module.rules.push({
test: /\.scss$/,
use: ['style-loader', 'css-loader', 'sass-loader'],
include: path.resolve(__dirname, '../'),
resolve: {
alias: {
"@": path.resolve(__dirname, "../src/"),
},
},
});
return config;
},
"stories": [
Expand Down
2 changes: 2 additions & 0 deletions mathesar_ui/.storybook/preview.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import "../src/component-library/styles.scss";

export const parameters = {
actions: { argTypesRegex: "^on[A-Z].*" },
controls: {
Expand Down
971 changes: 365 additions & 606 deletions mathesar_ui/package-lock.json

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions mathesar_ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
"@vitejs/plugin-legacy": "^1.5.2",
"babel-jest": "^26.6.3",
"babel-loader": "^8.2.2",
"css-loader": "^6.5.1",
"eslint": "^7.29.0",
"eslint-config-airbnb-typescript": "^12.3.1",
"eslint-import-resolver-typescript": "^2.4.0",
Expand All @@ -48,6 +49,8 @@
"eslint-plugin-svelte3": "^3.2.0",
"jest": "^26.6.3",
"sass": "^1.35.1",
"sass-loader": "^12.4.0",
"style-loader": "^3.3.1",
"svelte": "^3.42.4",
"svelte-check": "^2.2.5",
"svelte-jester": "^1.7.0",
Expand Down
10 changes: 10 additions & 0 deletions mathesar_ui/src/App.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,16 @@
</Route>
</section>

<!--
Supporting aliases in scss within the preprocessor is a bit of work.
I looked around to try to get it done but it didn't seem important to
spend time figuring this out.

The component-library style import would only ever be from App.svelte
and when the library is moved to a separate package, we wouldn't have to
worry about aliases.
-->
<style global lang="scss">
@import "component-library/styles.scss";
@import "App.scss";
</style>
4 changes: 0 additions & 4 deletions mathesar_ui/src/component-library/button/Button.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,3 @@
on:keydown>
<slot></slot>
</button>

<style global lang="scss">
@import "Button.scss";
</style>
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,3 @@
disabled={isProcessing || !canProceed}
/>
</div>

<style global lang='scss'>
@import './CancelOrProceedButtonPair.scss';
</style>
14 changes: 4 additions & 10 deletions mathesar_ui/src/component-library/checkbox/Checkbox.svelte
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
<script lang="ts">
import { createEventDispatcher } from 'svelte';
import { getLabelControllerFromContainingLabel } from '@mathesar-component-library-dir/label/LabelController';
import { getGloballyUniqueId } from '@mathesar-component-library-dir/common/utils/domUtils';
import BaseInput from '@mathesar-component-library-dir/common/base-components/BaseInput.svelte';

const dispatch = createEventDispatcher();

Expand All @@ -25,12 +24,9 @@
export let allowIndeterminate = false;
export let value: string | number | string[] | undefined = undefined;
export let disabled = false;
export let id = getGloballyUniqueId();
export let labelController = getLabelControllerFromContainingLabel();
export let id: string = undefined;

$: indeterminate = allowIndeterminate && checked === null;
$: labelController?.disabled.set(disabled);
$: labelController?.inputId.set(id);

function onChange(e: Event) {
checked = !checked;
Expand All @@ -41,6 +37,8 @@
}
</script>

<BaseInput {...$$restProps} bind:id {disabled}/>

<input
class="checkbox"
type="checkbox"
Expand All @@ -51,7 +49,3 @@
{value}
on:change={onChange}
/>

<style global lang="scss">
@import "Checkbox.scss";
</style>
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<script lang="ts">
import { getLabelControllerFromContainingLabel } from '@mathesar-component-library-dir/label/LabelController';
import { getGloballyUniqueId } from '@mathesar-component-library-dir/common/utils/domUtils';

export let id = getGloballyUniqueId();
export let labelController = getLabelControllerFromContainingLabel();
export let disabled = false;

$: labelController?.disabled.set(disabled);
$: labelController?.inputId.set(id);
</script>
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
.input-element {
border: 1px solid #dfdfdf;
transition: all .2s;
align-items: center;
border-radius: 0.25rem;
background: #fff;
margin: 0;
padding: 6px 8px;
line-height: inherit;
font-family: inherit;
font-size: inherit;
display: block;
width: 100%;
box-sizing: border-box;

&:focus {
border-color: #489ee4;
outline: 0;
box-shadow: 0 0 0 2px #2087e633;
}

&:disabled {
background: #efefef;
}
}
1 change: 1 addition & 0 deletions mathesar_ui/src/component-library/common/styles/main.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
@import "components/input.scss";
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
/**
* File to place common css & scss variables for the component library
*/
7 changes: 2 additions & 5 deletions mathesar_ui/src/component-library/dropdown/Dropdown.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,8 @@
</script>

<Button bind:element={triggerElement} appearance={triggerAppearance} class={tgClasses} on:click={toggle}
aria-controls={ariaControls} aria-haspopup="listbox" aria-label={ariaLabel} {size} on:keydown>
aria-controls={ariaControls} aria-haspopup="listbox" aria-label={ariaLabel} {size} on:keydown
{...$$restProps}>
<span class="label">
<slot name="trigger"></slot>
</span>
Expand All @@ -123,7 +124,3 @@
<slot name="content"></slot>
</div>
{/if}

<style global lang="scss">
@import "Dropdown.scss";
</style>
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<script lang='ts'>
import Checkbox from '@mathesar-component-library-dir/checkbox/Checkbox.svelte';
import type { DynamicInputInterfaceType } from './types.d';

export let value = undefined;
export let interfaceType: DynamicInputInterfaceType = undefined;
</script>

{#if interfaceType === 'toggle'}
<!--TODO: Add css to checkbox to show a toggle view -->
Toggle not implemented yet
{:else}
<Checkbox {...$$restProps} bind:checked={value}/>
{/if}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
<script lang='ts'>
import EnumInput from './EnumInput.svelte';
import StringInput from './StringInput..svelte';
import BooleanInput from './BooleanInput.svelte';
import NumberInput from '../number-input/NumberInput.svelte';
import type {
DynamicInputDataType,
DynamicInputInterfaceType,
DynamicInputSelectElement,
} from './types.d';

/**
* Type of input, one of: 'boolean', 'integer', 'float', 'string', 'date', 'datetime', 'time'
*/
export let dataType: DynamicInputDataType;

/**
* Value of input. Depends on type.
*/
export let value = undefined;

/**
* DOM input type for input.<br/>
* boolean -> checkbox, toggle, select. Default: checkbox.<br/>
* string -> text, textarea, select. Default: text.
*/
export let interfaceType: DynamicInputInterfaceType = undefined;

let enumValues: unknown[] = undefined;

/**
* Applies when interfaceType is select. List of values to allow for value.
*/
export { enumValues as enum };

/**
* Applies when interfaceType is select. Additional configuration for options
* that are displayed.
*/
export let options: DynamicInputSelectElement['options'] = undefined;
</script>

{#if enumValues || interfaceType === 'select'}
<EnumInput {...$$restProps} {enumValues} {dataType} {options} bind:value/>
{:else if dataType === 'boolean'}
<BooleanInput {...$$restProps} {interfaceType} bind:value/>
{:else if dataType === 'integer' || dataType === 'float'}
<NumberInput {...$$restProps} isInteger={dataType === 'integer'} bind:value/>
{:else if dataType === 'string'}
<StringInput {...$$restProps} {interfaceType} bind:value/>
{/if}
24 changes: 24 additions & 0 deletions mathesar_ui/src/component-library/dynamic-input/EnumInput.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<script lang='ts'>
import Select from '@mathesar-component-library-dir/select/Select.svelte';
import type { Appearance } from '@mathesar-component-library-dir/types.d';
import { generateSelectOptions, getSelectedValue, getInitialValue } from './utils';
import type { DynamicInputDataType, DynamicInputSelectElement } from './types.d';

export let dataType: DynamicInputDataType;
export let enumValues: unknown[] = undefined;
export let options: DynamicInputSelectElement['options'] = undefined;
export let triggerAppearance: Appearance = 'default';
export let value = getInitialValue(dataType, enumValues);

$: selectOptions = generateSelectOptions(dataType, enumValues, options);
$: selectedValue = getSelectedValue(selectOptions, value);

// TODO: Handle indeterminate state for boolean

function onChange(e: CustomEvent<{ option: { value: unknown } }>) {
value = e.detail?.option.value;
}
</script>

<Select idKey="value" {...$$restProps} {triggerAppearance}
options={selectOptions} value={selectedValue} on:change={onChange}/>
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<script lang='ts'>
import TextInput from '@mathesar-component-library-dir/text-input/TextInput.svelte';
import TextArea from '@mathesar-component-library-dir/text-area/TextArea.svelte';
import type { DynamicInputInterfaceType } from './types.d';

export let value = undefined;
export let interfaceType: DynamicInputInterfaceType = undefined;
</script>

{#if interfaceType === 'textarea'}
<TextArea {...$$restProps} bind:value/>
{:else}
<TextInput {...$$restProps} bind:value/>
{/if}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { Story, Canvas, ArgsTable, Source } from '@storybook/addon-docs/blocks';
import DynamicInput from '../DynamicInput.svelte';

# DynamicInput

DynamicInput system design specification.

## Usage

### Boolean type
<Canvas>
<Story name="boolean" id="components-dynamicinput--boolean" />
</Canvas>

### String type
<Canvas>
<Story name="string" id="components-dynamicinput--string" />
</Canvas>

## Arguments
<ArgsTable of={DynamicInput} />
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<script lang="ts">
import { Meta, Story } from '@storybook/addon-svelte-csf';
import Docs from './DynamicInput.mdx';
import DynamicInput from '../DynamicInput.svelte';

const meta = {
title: 'Components/DynamicInput',
parameters: {
docs: {
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
page: Docs,
source: {
type: 'code',
},
},
},
};
</script>

<Meta {...meta} />

<Story name="Boolean">
<DynamicInput dataType="boolean"/>
<DynamicInput dataType="boolean" interfaceType="select"/>
</Story>

<Story name="String">
<DynamicInput dataType="string"/>
<DynamicInput dataType="string" interfaceType="textarea"/>
<DynamicInput dataType="string" enum={['Pichu', 'Pikachu', 'Raichu']}/>
</Story>
19 changes: 19 additions & 0 deletions mathesar_ui/src/component-library/dynamic-input/types.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import type { SelectOption } from '@mathesar-component-library-dir/select/Select.d';

export type DynamicInputDataType =
'boolean' | 'integer' | 'float' | 'string' | 'date' | 'datetime' | 'time';

export type DynamicInputInterfaceType =
'text' | 'textarea' | 'number' | 'checkbox' | 'toggle' | 'select';

export interface DynamicInputSelectElement {
interfaceType: 'select',
options?: Record<string, {
label?: string,
}>
}

export interface EnumSelectOption extends SelectOption<unknown> {
value: unknown,
label: string
}
Loading