diff --git a/site/content/docs/02-template-syntax.md b/site/content/docs/02-template-syntax.md index aeabeb7a5..e6db1854d 100644 --- a/site/content/docs/02-template-syntax.md +++ b/site/content/docs/02-template-syntax.md @@ -821,6 +821,46 @@ A `class:` directive provides a shorter way of toggling a class on an element.
...
``` +#### style:*property* + +```sv +style:property={value} +``` +```sv +style:property="value" +``` +```sv +style:property +``` + +--- + +The `style:` directive provides a shorthand for setting multiple styles on an element. + +```sv + +
...
+
...
+ + +
...
+ + +
...
+ + +
...
+``` + +--- + +When `style:` directives are combined with `style` attributes, the directives will take precedence: + +```sv +
This will be red
+``` + + #### use:*action* diff --git a/src/compiler/compile/nodes/Element.ts b/src/compiler/compile/nodes/Element.ts index bf81599bf..c6b53b2e0 100644 --- a/src/compiler/compile/nodes/Element.ts +++ b/src/compiler/compile/nodes/Element.ts @@ -7,6 +7,7 @@ import Transition from './Transition'; import Animation from './Animation'; import Action from './Action'; import Class from './Class'; +import Style from './Style'; import Text from './Text'; import { namespaces } from '../../utils/namespaces'; import map_children from './shared/map_children'; @@ -180,6 +181,7 @@ export default class Element extends Node { actions: Action[] = []; bindings: Binding[] = []; classes: Class[] = []; + styles: Style[] = []; handlers: EventHandler[] = []; lets: Let[] = []; intro?: Transition = null; @@ -263,6 +265,10 @@ export default class Element extends Node { this.classes.push(new Class(component, this, scope, node)); break; + case 'Style': + this.styles.push(new Style(component, this, scope, node)); + break; + case 'EventHandler': this.handlers.push(new EventHandler(component, this, scope, node)); break; diff --git a/src/compiler/compile/nodes/Style.ts b/src/compiler/compile/nodes/Style.ts new file mode 100644 index 000000000..e922f6f57 --- /dev/null +++ b/src/compiler/compile/nodes/Style.ts @@ -0,0 +1,22 @@ +import Node from './shared/Node'; +import Expression from './shared/Expression'; +import { TemplateNode } from '../../interfaces'; +import TemplateScope from './shared/TemplateScope'; +import Component from '../Component'; + +export default class Style extends Node { + type: 'Style'; + name: string; + expression: Expression; + should_cache: boolean; + + constructor(component: Component, parent: Node, scope: TemplateScope, info: TemplateNode) { + super(component, parent, scope, info); + + this.name = info.name; + + this.expression = new Expression(component, this, scope, info.expression); + + this.should_cache = info.expression.type === 'TemplateLiteral' && info.expression.expressions.length > 0; + } +} diff --git a/src/compiler/compile/nodes/interfaces.ts b/src/compiler/compile/nodes/interfaces.ts index a98c21511..2e2fa8b5e 100644 --- a/src/compiler/compile/nodes/interfaces.ts +++ b/src/compiler/compile/nodes/interfaces.ts @@ -8,6 +8,7 @@ import Binding from './Binding'; import Body from './Body'; import CatchBlock from './CatchBlock'; import Class from './Class'; +import Style from './Style'; import Comment from './Comment'; import DebugTag from './DebugTag'; import EachBlock from './EachBlock'; @@ -62,6 +63,7 @@ export type INode = Action | Slot | SlotTemplate | DefaultSlotTemplate +| Style | Tag | Text | ThenBlock diff --git a/src/compiler/compile/render_dom/wrappers/Element/index.ts b/src/compiler/compile/render_dom/wrappers/Element/index.ts index 583274044..59d06b8c9 100644 --- a/src/compiler/compile/render_dom/wrappers/Element/index.ts +++ b/src/compiler/compile/render_dom/wrappers/Element/index.ts @@ -340,6 +340,7 @@ export default class ElementWrapper extends Wrapper { this.add_transitions(block); this.add_animation(block); this.add_classes(block); + this.add_styles(block); this.add_manual_style_scoping(block); if (nodes && this.renderer.options.hydratable && !this.void) { @@ -914,6 +915,43 @@ export default class ElementWrapper extends Wrapper { }); } + add_styles(block: Block) { + const has_spread = this.node.attributes.some(attr => attr.is_spread); + this.node.styles.forEach((style_directive) => { + const { name, expression, should_cache } = style_directive; + + const snippet = expression.manipulate(block); + let cached_snippet; + if (should_cache) { + cached_snippet = block.get_unique_name(`style_${name}`); + block.add_variable(cached_snippet, snippet); + } + + const updater = b`@set_style(${this.var}, "${name}", ${should_cache ? cached_snippet : snippet}, false)`; + + block.chunks.hydrate.push(updater); + + const dependencies = expression.dynamic_dependencies(); + if (has_spread) { + block.chunks.update.push(updater); + } else if (dependencies.length > 0) { + if (should_cache) { + block.chunks.update.push(b` + if (${block.renderer.dirty(dependencies)} && (${cached_snippet} !== (${cached_snippet} = ${snippet}))) { + ${updater} + } + `); + } else { + block.chunks.update.push(b` + if (${block.renderer.dirty(dependencies)}) { + ${updater} + } + `); + } + } + }); + } + add_manual_style_scoping(block) { if (this.node.needs_manual_style_scoping) { const updater = b`@toggle_class(${this.var}, "${this.node.component.stylesheet.id}", true);`; diff --git a/src/compiler/compile/render_ssr/handlers/Element.ts b/src/compiler/compile/render_ssr/handlers/Element.ts index c0bf826a8..dd096ae79 100644 --- a/src/compiler/compile/render_ssr/handlers/Element.ts +++ b/src/compiler/compile/render_ssr/handlers/Element.ts @@ -3,7 +3,7 @@ import { get_attribute_expression, get_attribute_value, get_class_attribute_valu import { boolean_attributes } from './shared/boolean_attributes'; import Renderer, { RenderOptions } from '../Renderer'; import Element from '../../nodes/Element'; -import { x } from 'code-red'; +import { p, x } from 'code-red'; import Expression from '../../nodes/shared/Expression'; import remove_whitespace_children from './utils/remove_whitespace_children'; import fix_attribute_casing from '../../render_dom/wrappers/Element/fix_attribute_casing'; @@ -36,6 +36,15 @@ export default function(node: Element, renderer: Renderer, options: RenderOption class_expression_list.length > 0 && class_expression_list.reduce((lhs, rhs) => x`${lhs} + ' ' + ${rhs}`); + const style_expression_list = node.styles.map(style_directive => { + const { name, expression: { node: expression } } = style_directive; + return p`"${name}": ${expression}`; + }); + + const style_expression = + style_expression_list.length > 0 && + x`{ ${style_expression_list} }`; + if (node.attributes.some(attr => attr.is_spread)) { // TODO dry this out const args = []; @@ -65,9 +74,10 @@ export default function(node: Element, renderer: Renderer, options: RenderOption } }); - renderer.add_expression(x`@spread([${args}], ${class_expression})`); + renderer.add_expression(x`@spread([${args}], { classes: ${class_expression}, styles: ${style_expression} })`); } else { let add_class_attribute = !!class_expression; + let add_style_attribute = !!style_expression; node.attributes.forEach(attribute => { const name = attribute.name.toLowerCase(); const attr_name = node.namespace === namespaces.foreign ? attribute.name : fix_attribute_casing(attribute.name); @@ -88,6 +98,9 @@ export default function(node: Element, renderer: Renderer, options: RenderOption renderer.add_string(` ${attr_name}="`); renderer.add_expression(x`[${get_class_attribute_value(attribute)}, ${class_expression}].join(' ').trim()`); renderer.add_string('"'); + } else if (name === 'style' && style_expression) { + add_style_attribute = false; + renderer.add_expression(x`@add_styles(@merge_ssr_styles(${get_attribute_value(attribute)}, ${style_expression}))`); } else if (attribute.chunks.length === 1 && attribute.chunks[0].type !== 'Text') { const snippet = (attribute.chunks[0] as Expression).node; renderer.add_expression(x`@add_attribute("${attr_name}", ${snippet}, ${boolean_attributes.has(name) ? 1 : 0})`); @@ -98,7 +111,10 @@ export default function(node: Element, renderer: Renderer, options: RenderOption } }); if (add_class_attribute) { - renderer.add_expression(x`@add_classes([${class_expression}].join(' ').trim())`); + renderer.add_expression(x`@add_classes((${class_expression}).trim())`); + } + if (add_style_attribute) { + renderer.add_expression(x`@add_styles(${style_expression})`); } } diff --git a/src/compiler/interfaces.ts b/src/compiler/interfaces.ts index b999fbd80..3e54ea55a 100644 --- a/src/compiler/interfaces.ts +++ b/src/compiler/interfaces.ts @@ -34,6 +34,7 @@ export type DirectiveType = 'Action' | 'Animation' | 'Binding' | 'Class' +| 'Style' | 'EventHandler' | 'Let' | 'Ref' diff --git a/src/compiler/parse/state/tag.ts b/src/compiler/parse/state/tag.ts index 832f47aac..81d8fff1c 100644 --- a/src/compiler/parse/state/tag.ts +++ b/src/compiler/parse/state/tag.ts @@ -1,10 +1,11 @@ +import { TemplateLiteral, TemplateElement, Expression } from 'estree'; import read_expression from '../read/expression'; import read_script from '../read/script'; import read_style from '../read/style'; import { decode_character_references, closing_tag_omitted } from '../utils/html'; import { is_void } from '../../utils/names'; import { Parser } from '../index'; -import { Directive, DirectiveType, TemplateNode, Text } from '../../interfaces'; +import { Directive, DirectiveType, TemplateNode, Text, MustacheTag } from '../../interfaces'; import fuzzymatch from '../../utils/fuzzymatch'; import { extract_svelte_ignore } from '../../utils/extract_svelte_ignore'; import parser_errors from '../errors'; @@ -270,6 +271,36 @@ function read_tag_name(parser: Parser) { return name; } +function node_to_template_literal(value: Array): TemplateLiteral { + let quasi: TemplateElement = { + type: 'TemplateElement', + value: { raw: '', cooked: null }, + tail: false + }; + const literal: TemplateLiteral = { + type: 'TemplateLiteral', + expressions: [], + quasis: [] + }; + + value.forEach((node) => { + if (node.type === 'Text') { + quasi.value.raw += node.raw; + } else if (node.type === 'MustacheTag') { + literal.quasis.push(quasi); + literal.expressions.push(node.expression as Expression); + quasi = { + type: 'TemplateElement', + value: { raw: '', cooked: null }, + tail: false + }; + } + }); + quasi.tail = true; + literal.quasis.push(quasi); + return literal; +} + function read_attribute(parser: Parser, unique_names: Set) { const start = parser.index; @@ -365,9 +396,17 @@ function read_attribute(parser: Parser, unique_names: Set) { parser.error(parser_errors.invalid_ref_directive(directive_name), start); } - if (value[0]) { - if ((value as any[]).length > 1 || value[0].type === 'Text') { - parser.error(parser_errors.invalid_directive_value, value[0].start); + const first_value = value[0]; + let expression = null; + + if (first_value) { + const attribute_contains_text = (value as any[]).length > 1 || first_value.type === 'Text'; + if (type === 'Style') { + expression = attribute_contains_text ? node_to_template_literal(value as Array) : first_value.expression; + } else if (attribute_contains_text) { + parser.error(parser_errors.invalid_directive_value, first_value.start); + } else { + expression = first_value.expression; } } @@ -377,7 +416,7 @@ function read_attribute(parser: Parser, unique_names: Set) { type, name: directive_name, modifiers, - expression: (value[0] && value[0].expression) || null + expression }; if (type === 'Transition') { @@ -386,7 +425,8 @@ function read_attribute(parser: Parser, unique_names: Set) { directive.outro = direction === 'out' || direction === 'transition'; } - if (!directive.expression && (type === 'Binding' || type === 'Class')) { + // Directive name is expression, e.g.

+ if (!directive.expression && (type === 'Binding' || type === 'Class' || type === 'Style')) { directive.expression = { start: directive.start + colon_index + 1, end: directive.end, @@ -414,6 +454,7 @@ function get_directive_type(name: string): DirectiveType { if (name === 'animate') return 'Animation'; if (name === 'bind') return 'Binding'; if (name === 'class') return 'Class'; + if (name === 'style') return 'Style'; if (name === 'on') return 'EventHandler'; if (name === 'let') return 'Let'; if (name === 'ref') return 'Ref'; @@ -471,6 +512,8 @@ function read_sequence(parser: Parser, done: () => boolean): TemplateNode[] { data: null }; + const chunks: TemplateNode[] = []; + function flush(end: number) { if (current_chunk.raw) { current_chunk.data = decode_character_references(current_chunk.raw); @@ -479,8 +522,6 @@ function read_sequence(parser: Parser, done: () => boolean): TemplateNode[] { } } - const chunks: TemplateNode[] = []; - while (parser.index < parser.template.length) { const index = parser.index; diff --git a/src/runtime/internal/dom.ts b/src/runtime/internal/dom.ts index eb3389e3f..a8b542053 100644 --- a/src/runtime/internal/dom.ts +++ b/src/runtime/internal/dom.ts @@ -530,7 +530,11 @@ export function set_input_type(input, type) { } export function set_style(node, key, value, important) { - node.style.setProperty(key, value, important ? 'important' : ''); + if (value === null) { + node.style.removeProperty(key); + } else { + node.style.setProperty(key, value, important ? 'important' : ''); + } } export function select_option(select, value) { diff --git a/src/runtime/internal/ssr.ts b/src/runtime/internal/ssr.ts index 4d91857ad..aeaf6e8de 100644 --- a/src/runtime/internal/ssr.ts +++ b/src/runtime/internal/ssr.ts @@ -6,15 +6,29 @@ export const invalid_attribute_name_character = /[\s'">/=\u{FDD0}-\u{FDEF}\u{FFF // https://html.spec.whatwg.org/multipage/syntax.html#attributes-2 // https://infra.spec.whatwg.org/#noncharacter -export function spread(args, classes_to_add) { +export function spread(args, attrs_to_add) { const attributes = Object.assign({}, ...args); - if (classes_to_add) { - if (attributes.class == null) { - attributes.class = classes_to_add; - } else { - attributes.class += ' ' + classes_to_add; + if (attrs_to_add) { + const classes_to_add = attrs_to_add.classes; + const styles_to_add = attrs_to_add.styles; + + if (classes_to_add) { + if (attributes.class == null) { + attributes.class = classes_to_add; + } else { + attributes.class += ' ' + classes_to_add; + } + } + + if (styles_to_add) { + if (attributes.style == null) { + attributes.style = style_object_to_string(styles_to_add); + } else { + attributes.style = style_object_to_string(merge_ssr_styles(attributes.style, styles_to_add)); + } } } + let str = ''; Object.keys(attributes).forEach(name => { @@ -32,6 +46,28 @@ export function spread(args, classes_to_add) { return str; } +export function merge_ssr_styles(style_attribute, style_directive) { + const style_object = {}; + for (const individual_style of style_attribute.split(';')) { + const colon_index = individual_style.indexOf(':'); + const name = individual_style.slice(0, colon_index).trim(); + const value = individual_style.slice(colon_index + 1).trim(); + if (!name) continue; + style_object[name] = value; + } + + for (const name in style_directive) { + const value = style_directive[name]; + if (value) { + style_object[name] = value; + } else { + delete style_object[name]; + } + } + + return style_object; +} + export const escaped = { '"': '"', "'": ''', @@ -147,3 +183,16 @@ export function add_attribute(name, value, boolean) { export function add_classes(classes) { return classes ? ` class="${classes}"` : ''; } + +function style_object_to_string(style_object) { + return Object.keys(style_object) + .filter(key => style_object[key]) + .map(key => `${key}: ${style_object[key]};`) + .join(' '); +} + +export function add_styles(style_object) { + const styles = style_object_to_string(style_object); + + return styles ? ` style="${styles}"` : ''; +} diff --git a/test/parser/samples/attribute-class-directive/input.svelte b/test/parser/samples/attribute-class-directive/input.svelte new file mode 100644 index 000000000..629df2e29 --- /dev/null +++ b/test/parser/samples/attribute-class-directive/input.svelte @@ -0,0 +1 @@ +

\ No newline at end of file diff --git a/test/parser/samples/attribute-class-directive/output.json b/test/parser/samples/attribute-class-directive/output.json new file mode 100644 index 000000000..abb2e0c0b --- /dev/null +++ b/test/parser/samples/attribute-class-directive/output.json @@ -0,0 +1,41 @@ +{ + "html": { + "start": 0, + "end": 29, + "type": "Fragment", + "children": [ + { + "start": 0, + "end": 29, + "type": "Element", + "name": "div", + "attributes": [ + { + "start": 5, + "end": 22, + "type": "Class", + "name": "foo", + "modifiers": [], + "expression": { + "type": "Identifier", + "start": 16, + "end": 21, + "loc": { + "start": { + "line": 1, + "column": 16 + }, + "end": { + "line": 1, + "column": 21 + } + }, + "name": "isFoo" + } + } + ], + "children": [] + } + ] + } +} \ No newline at end of file diff --git a/test/parser/samples/attribute-style-directive-shorthand/input.svelte b/test/parser/samples/attribute-style-directive-shorthand/input.svelte new file mode 100644 index 000000000..3e2c66f21 --- /dev/null +++ b/test/parser/samples/attribute-style-directive-shorthand/input.svelte @@ -0,0 +1 @@ +
\ No newline at end of file diff --git a/test/parser/samples/attribute-style-directive-shorthand/output.json b/test/parser/samples/attribute-style-directive-shorthand/output.json new file mode 100644 index 000000000..d4e81b6e4 --- /dev/null +++ b/test/parser/samples/attribute-style-directive-shorthand/output.json @@ -0,0 +1,31 @@ +{ + "html": { + "start": 0, + "end": 23, + "type": "Fragment", + "children": [ + { + "start": 0, + "end": 23, + "type": "Element", + "name": "div", + "attributes": [ + { + "start": 5, + "end": 16, + "type": "Style", + "name": "color", + "modifiers": [], + "expression": { + "start": 11, + "end": 16, + "name": "color", + "type": "Identifier" + } + } + ], + "children": [] + } + ] + } +} diff --git a/test/parser/samples/attribute-style-directive-string/input.svelte b/test/parser/samples/attribute-style-directive-string/input.svelte new file mode 100644 index 000000000..b2eb6bfef --- /dev/null +++ b/test/parser/samples/attribute-style-directive-string/input.svelte @@ -0,0 +1 @@ +
\ No newline at end of file diff --git a/test/parser/samples/attribute-style-directive-string/output.json b/test/parser/samples/attribute-style-directive-string/output.json new file mode 100644 index 000000000..4cf2bd30c --- /dev/null +++ b/test/parser/samples/attribute-style-directive-string/output.json @@ -0,0 +1,39 @@ +{ + "html": { + "start": 0, + "end": 29, + "type": "Fragment", + "children": [ + { + "start": 0, + "end": 29, + "type": "Element", + "name": "div", + "attributes": [ + { + "start": 5, + "end": 22, + "type": "Style", + "name": "color", + "modifiers": [], + "expression": { + "type": "TemplateLiteral", + "expressions": [], + "quasis": [ + { + "type": "TemplateElement", + "value": { + "raw": "red", + "cooked": null + }, + "tail": true + } + ] + } + } + ], + "children": [] + } + ] + } +} \ No newline at end of file diff --git a/test/parser/samples/attribute-style-directive/input.svelte b/test/parser/samples/attribute-style-directive/input.svelte new file mode 100644 index 000000000..536d16232 --- /dev/null +++ b/test/parser/samples/attribute-style-directive/input.svelte @@ -0,0 +1 @@ +
\ No newline at end of file diff --git a/test/parser/samples/attribute-style-directive/output.json b/test/parser/samples/attribute-style-directive/output.json new file mode 100644 index 000000000..36906045c --- /dev/null +++ b/test/parser/samples/attribute-style-directive/output.json @@ -0,0 +1,41 @@ +{ + "html": { + "start": 0, + "end": 33, + "type": "Fragment", + "children": [ + { + "start": 0, + "end": 33, + "type": "Element", + "name": "div", + "attributes": [ + { + "start": 5, + "end": 26, + "type": "Style", + "name": "color", + "modifiers": [], + "expression": { + "type": "Identifier", + "start": 18, + "end": 25, + "loc": { + "start": { + "line": 1, + "column": 18 + }, + "end": { + "line": 1, + "column": 25 + } + }, + "name": "myColor" + } + } + ], + "children": [] + } + ] + } +} \ No newline at end of file diff --git a/test/parser/samples/attribute-style/input.svelte b/test/parser/samples/attribute-style/input.svelte new file mode 100644 index 000000000..12872605a --- /dev/null +++ b/test/parser/samples/attribute-style/input.svelte @@ -0,0 +1 @@ +
red
\ No newline at end of file diff --git a/test/parser/samples/attribute-style/output.json b/test/parser/samples/attribute-style/output.json new file mode 100644 index 000000000..95c0c8a7a --- /dev/null +++ b/test/parser/samples/attribute-style/output.json @@ -0,0 +1,41 @@ +{ + "html": { + "start": 0, + "end": 34, + "type": "Fragment", + "children": [ + { + "start": 0, + "end": 34, + "type": "Element", + "name": "div", + "attributes": [ + { + "start": 5, + "end": 24, + "type": "Attribute", + "name": "style", + "value": [ + { + "start": 12, + "end": 23, + "type": "Text", + "raw": "color: red;", + "data": "color: red;" + } + ] + } + ], + "children": [ + { + "start": 25, + "end": 28, + "type": "Text", + "raw": "red", + "data": "red" + } + ] + } + ] + } +} \ No newline at end of file diff --git a/test/runtime/samples/inline-style-directive-and-style-attr-merged/_config.js b/test/runtime/samples/inline-style-directive-and-style-attr-merged/_config.js new file mode 100644 index 000000000..f5780daf8 --- /dev/null +++ b/test/runtime/samples/inline-style-directive-and-style-attr-merged/_config.js @@ -0,0 +1,13 @@ +export default { + html: ` +

+ `, + + test({ assert, target, window }) { + const p = target.querySelector('p'); + + const styles = window.getComputedStyle(p); + assert.equal(styles.color, 'red'); + assert.equal(styles.height, '40px'); + } +}; diff --git a/test/runtime/samples/inline-style-directive-and-style-attr-merged/main.svelte b/test/runtime/samples/inline-style-directive-and-style-attr-merged/main.svelte new file mode 100644 index 000000000..6f3e86b9b --- /dev/null +++ b/test/runtime/samples/inline-style-directive-and-style-attr-merged/main.svelte @@ -0,0 +1,5 @@ + + +

diff --git a/test/runtime/samples/inline-style-directive-and-style-attr/_config.js b/test/runtime/samples/inline-style-directive-and-style-attr/_config.js new file mode 100644 index 000000000..f5780daf8 --- /dev/null +++ b/test/runtime/samples/inline-style-directive-and-style-attr/_config.js @@ -0,0 +1,13 @@ +export default { + html: ` +

+ `, + + test({ assert, target, window }) { + const p = target.querySelector('p'); + + const styles = window.getComputedStyle(p); + assert.equal(styles.color, 'red'); + assert.equal(styles.height, '40px'); + } +}; diff --git a/test/runtime/samples/inline-style-directive-and-style-attr/main.svelte b/test/runtime/samples/inline-style-directive-and-style-attr/main.svelte new file mode 100644 index 000000000..e2ba0fa59 --- /dev/null +++ b/test/runtime/samples/inline-style-directive-and-style-attr/main.svelte @@ -0,0 +1,5 @@ + + +

diff --git a/test/runtime/samples/inline-style-directive-css-vars/_config.js b/test/runtime/samples/inline-style-directive-css-vars/_config.js new file mode 100644 index 000000000..c8b239a42 --- /dev/null +++ b/test/runtime/samples/inline-style-directive-css-vars/_config.js @@ -0,0 +1,9 @@ +export default { + html: '

', + + test({ assert, component, target }) { + component.myColor = 'blue'; + + assert.htmlEqual(target.innerHTML, '

'); + } +}; diff --git a/test/runtime/samples/inline-style-directive-css-vars/main.svelte b/test/runtime/samples/inline-style-directive-css-vars/main.svelte new file mode 100644 index 000000000..689a2333f --- /dev/null +++ b/test/runtime/samples/inline-style-directive-css-vars/main.svelte @@ -0,0 +1,5 @@ + + +

diff --git a/test/runtime/samples/inline-style-directive-dynamic/_config.js b/test/runtime/samples/inline-style-directive-dynamic/_config.js new file mode 100644 index 000000000..a2214fd87 --- /dev/null +++ b/test/runtime/samples/inline-style-directive-dynamic/_config.js @@ -0,0 +1,10 @@ +export default { + html: ` +

+ `, + + test({ assert, component, target }) { + component.myColor = 'blue'; + assert.htmlEqual(target.innerHTML, '

'); + } +}; diff --git a/test/runtime/samples/inline-style-directive-dynamic/main.svelte b/test/runtime/samples/inline-style-directive-dynamic/main.svelte new file mode 100644 index 000000000..1d60aefa6 --- /dev/null +++ b/test/runtime/samples/inline-style-directive-dynamic/main.svelte @@ -0,0 +1,5 @@ + + +

diff --git a/test/runtime/samples/inline-style-directive-multiple/_config.js b/test/runtime/samples/inline-style-directive-multiple/_config.js new file mode 100644 index 000000000..725d03c4f --- /dev/null +++ b/test/runtime/samples/inline-style-directive-multiple/_config.js @@ -0,0 +1,27 @@ +export default { + html: ` +

+ `, + + test({ assert, component, target, window }) { + const p = target.querySelector('p'); + + let styles = window.getComputedStyle(p); + assert.equal(styles.color, 'red'); + + component.myColor = 'pink'; + component.width = '100vh'; + component.absolute = true; + component.bold = false; + + styles = window.getComputedStyle(p); + assert.htmlEqual( + target.innerHTML, + '

' + ); + assert.equal(styles.color, 'pink'); + assert.equal(styles.width, '100vh'); + assert.equal(styles.fontWeight, '100'); + assert.equal(styles.position, 'absolute'); + } +}; diff --git a/test/runtime/samples/inline-style-directive-multiple/main.svelte b/test/runtime/samples/inline-style-directive-multiple/main.svelte new file mode 100644 index 000000000..06f03206d --- /dev/null +++ b/test/runtime/samples/inline-style-directive-multiple/main.svelte @@ -0,0 +1,13 @@ + + +

diff --git a/test/runtime/samples/inline-style-directive-shorthand/_config.js b/test/runtime/samples/inline-style-directive-shorthand/_config.js new file mode 100644 index 000000000..64043e526 --- /dev/null +++ b/test/runtime/samples/inline-style-directive-shorthand/_config.js @@ -0,0 +1,18 @@ +export default { + html: ` +

+ `, + + test({ assert, component, target, window }) { + const p = target.querySelector('p'); + + let styles = window.getComputedStyle(p); + assert.equal(styles.color, 'red'); + + component.color = 'blue'; + assert.htmlEqual(target.innerHTML, '

'); + + styles = window.getComputedStyle(p); + assert.equal(styles.color, 'blue'); + } +}; diff --git a/test/runtime/samples/inline-style-directive-shorthand/main.svelte b/test/runtime/samples/inline-style-directive-shorthand/main.svelte new file mode 100644 index 000000000..65bbff910 --- /dev/null +++ b/test/runtime/samples/inline-style-directive-shorthand/main.svelte @@ -0,0 +1,5 @@ + + +

diff --git a/test/runtime/samples/inline-style-directive-spread-and-attr-empty/_config.js b/test/runtime/samples/inline-style-directive-spread-and-attr-empty/_config.js new file mode 100644 index 000000000..f3dcbc882 --- /dev/null +++ b/test/runtime/samples/inline-style-directive-spread-and-attr-empty/_config.js @@ -0,0 +1,5 @@ +export default { + html: ` +

+ ` +}; diff --git a/test/runtime/samples/inline-style-directive-spread-and-attr-empty/main.svelte b/test/runtime/samples/inline-style-directive-spread-and-attr-empty/main.svelte new file mode 100644 index 000000000..c2d5cb4d3 --- /dev/null +++ b/test/runtime/samples/inline-style-directive-spread-and-attr-empty/main.svelte @@ -0,0 +1,11 @@ + + +

diff --git a/test/runtime/samples/inline-style-directive-spread-and-attr/_config.js b/test/runtime/samples/inline-style-directive-spread-and-attr/_config.js new file mode 100644 index 000000000..b3660bd5c --- /dev/null +++ b/test/runtime/samples/inline-style-directive-spread-and-attr/_config.js @@ -0,0 +1,43 @@ +export default { + html: ` +

+ `, + + test({ assert, component, target, window }) { + const p = target.querySelector('p'); + + let styles = window.getComputedStyle(p); + assert.equal(styles.color, 'green'); + + component.color = null; + assert.htmlEqual(target.innerHTML, '

'); + styles = window.getComputedStyle(p); + assert.equal(styles.color, ''); + + component.spread = { style: 'color: yellow; padding: 30px;' }; + + assert.htmlEqual(target.innerHTML, '

'); + styles = window.getComputedStyle(p); + assert.equal(styles.color, ''); + assert.equal(styles.padding, '30px'); + + component.spread = {}; + component.style = 'color: blue; background-color: green;'; + assert.htmlEqual( + target.innerHTML, + '

' + ); + styles = window.getComputedStyle(p); + assert.equal(styles.color, ''); + assert.equal(styles.backgroundColor, 'green'); + + component.color = 'purple'; + assert.htmlEqual( + target.innerHTML, + '

' + ); + styles = window.getComputedStyle(p); + assert.equal(styles.color, 'purple'); + assert.equal(styles.backgroundColor, 'green'); + } +}; diff --git a/test/runtime/samples/inline-style-directive-spread-and-attr/main.svelte b/test/runtime/samples/inline-style-directive-spread-and-attr/main.svelte new file mode 100644 index 000000000..a9696df67 --- /dev/null +++ b/test/runtime/samples/inline-style-directive-spread-and-attr/main.svelte @@ -0,0 +1,11 @@ + + +

diff --git a/test/runtime/samples/inline-style-directive-spread-dynamic/_config.js b/test/runtime/samples/inline-style-directive-spread-dynamic/_config.js new file mode 100644 index 000000000..152e4036a --- /dev/null +++ b/test/runtime/samples/inline-style-directive-spread-dynamic/_config.js @@ -0,0 +1,39 @@ +export default { + html: ` +

+ `, + + test({ assert, component, target, window }) { + const p = target.querySelector('p'); + + const styles = window.getComputedStyle(p); + assert.equal(styles.color, 'blue'); + assert.equal(styles.width, '65px'); + assert.equal(p.id, 'my-id'); + + component.color = 'red'; + + assert.htmlEqual( + target.innerHTML, + '

' + ); + + component.obj = { style: 'height: 72px;' }; + + assert.htmlEqual( + target.innerHTML, + '

' + ); + + component.obj = { style: 'border-radius: 2px; color: orange' }; + + assert.htmlEqual( + target.innerHTML, + '

' + ); + + component.obj = {}; + + assert.htmlEqual(target.innerHTML, '

'); + } +}; diff --git a/test/runtime/samples/inline-style-directive-spread-dynamic/main.svelte b/test/runtime/samples/inline-style-directive-spread-dynamic/main.svelte new file mode 100644 index 000000000..a514e4856 --- /dev/null +++ b/test/runtime/samples/inline-style-directive-spread-dynamic/main.svelte @@ -0,0 +1,5 @@ + +

diff --git a/test/runtime/samples/inline-style-directive-spread/_config.js b/test/runtime/samples/inline-style-directive-spread/_config.js new file mode 100644 index 000000000..adb8a2654 --- /dev/null +++ b/test/runtime/samples/inline-style-directive-spread/_config.js @@ -0,0 +1,14 @@ +export default { + html: ` +

+ `, + + test({ assert, target, window }) { + const p = target.querySelector('p'); + + const styles = window.getComputedStyle(p); + assert.equal(styles.color, 'blue'); + assert.equal(styles.width, '65px'); + assert.equal(p.id, 'my-id'); + } +}; diff --git a/test/runtime/samples/inline-style-directive-spread/main.svelte b/test/runtime/samples/inline-style-directive-spread/main.svelte new file mode 100644 index 000000000..51291146c --- /dev/null +++ b/test/runtime/samples/inline-style-directive-spread/main.svelte @@ -0,0 +1 @@ +

diff --git a/test/runtime/samples/inline-style-directive-string-variable/_config.js b/test/runtime/samples/inline-style-directive-string-variable/_config.js new file mode 100644 index 000000000..411a42db8 --- /dev/null +++ b/test/runtime/samples/inline-style-directive-string-variable/_config.js @@ -0,0 +1,23 @@ +export default { + html: ` +

+ `, + + test({ assert, component, target, window }) { + const p = target.querySelector('p'); + + const styles = window.getComputedStyle(p); + assert.equal(styles.color, 'green'); + assert.equal(styles.transform, 'translateX(45px)'); + assert.equal(styles.border, '100px solid pink'); + + component.translate_x = '100%'; + component.border_width = 20; + component.border_color = 'yellow'; + + assert.htmlEqual( + target.innerHTML, + '

' + ); + } +}; diff --git a/test/runtime/samples/inline-style-directive-string-variable/main.svelte b/test/runtime/samples/inline-style-directive-string-variable/main.svelte new file mode 100644 index 000000000..1f0b5019f --- /dev/null +++ b/test/runtime/samples/inline-style-directive-string-variable/main.svelte @@ -0,0 +1,7 @@ + + +

diff --git a/test/runtime/samples/inline-style-directive-string/_config.js b/test/runtime/samples/inline-style-directive-string/_config.js new file mode 100644 index 000000000..ec32e157e --- /dev/null +++ b/test/runtime/samples/inline-style-directive-string/_config.js @@ -0,0 +1,12 @@ +export default { + html: ` +

+ `, + + test({ assert, target, window }) { + const p = target.querySelector('p'); + + const styles = window.getComputedStyle(p); + assert.equal(styles.color, 'red'); + } +}; diff --git a/test/runtime/samples/inline-style-directive-string/main.svelte b/test/runtime/samples/inline-style-directive-string/main.svelte new file mode 100644 index 000000000..4e7ce22b2 --- /dev/null +++ b/test/runtime/samples/inline-style-directive-string/main.svelte @@ -0,0 +1 @@ +

diff --git a/test/runtime/samples/inline-style-directive/_config.js b/test/runtime/samples/inline-style-directive/_config.js new file mode 100644 index 000000000..ec32e157e --- /dev/null +++ b/test/runtime/samples/inline-style-directive/_config.js @@ -0,0 +1,12 @@ +export default { + html: ` +

+ `, + + test({ assert, target, window }) { + const p = target.querySelector('p'); + + const styles = window.getComputedStyle(p); + assert.equal(styles.color, 'red'); + } +}; diff --git a/test/runtime/samples/inline-style-directive/main.svelte b/test/runtime/samples/inline-style-directive/main.svelte new file mode 100644 index 000000000..1d60aefa6 --- /dev/null +++ b/test/runtime/samples/inline-style-directive/main.svelte @@ -0,0 +1,5 @@ + + +

diff --git a/test/runtime/samples/inline-style/_config.js b/test/runtime/samples/inline-style/_config.js new file mode 100644 index 000000000..3e984d4c6 --- /dev/null +++ b/test/runtime/samples/inline-style/_config.js @@ -0,0 +1,12 @@ +export default { + html: ` +

+ `, + + test({ assert, component, target, window }) { + const p = target.querySelector('div'); + + const styles = window.getComputedStyle(p); + assert.equal(styles.color, 'red'); + } +}; diff --git a/test/runtime/samples/inline-style/main.svelte b/test/runtime/samples/inline-style/main.svelte new file mode 100644 index 000000000..0f37a04c9 --- /dev/null +++ b/test/runtime/samples/inline-style/main.svelte @@ -0,0 +1 @@ +
\ No newline at end of file