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

Add support for controlling the overrides of a Symbol #323

Merged
merged 5 commits into from Jan 8, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 2 additions & 0 deletions .eslintrc.yml
Expand Up @@ -144,6 +144,8 @@ globals:
MSExportManager: false
NSFileManager: false
NSDataWritingWithoutOverwriting: false
MSOverrideProperty: false
MSOverridePoint: false

rules:
###########
Expand Down
2 changes: 2 additions & 0 deletions CHANGELOG.json
Expand Up @@ -17,6 +17,8 @@
"[New] `export` can now export to the JSON file format",
"[New] Add `background` property on Artboard",
"[New] Add `transform` property on Layer",
"[New] Add `editable` property on Override",
"[New] Add `overrides` property on Symbol Master to be able to change their `editable` property",
"[Improved] Add an options parameter when detaching a Symbol Instance"
],
"releases": {
Expand Down
47 changes: 47 additions & 0 deletions Source/dom/layers/SymbolMaster.js
Expand Up @@ -5,6 +5,7 @@ import { Rectangle } from '../models/Rectangle'
import { Types } from '../enums'
import { Factory } from '../Factory'
import { wrapObject } from '../wrapNativeObject'
import { Override } from '../models/Override'

/**
* A Sketch symbol master.
Expand Down Expand Up @@ -121,6 +122,52 @@ SymbolMaster.define('symbolId', {
},
})

SymbolMaster.define('overrides', {
get() {
// undefined when immutable
if (!this._object.overrideProperies) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this a typo on this code, or on Sketch?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(The overrideProperies bit)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in sketch. There is even overrideProperiesByCombiningParentsOverrideProperties haha

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

¯_(ツ)_/¯

return undefined
}
const overrideProperies = this._object.overrideProperies()
const overrides = toArray(this._object.availableOverrides())

// recursively find the overrides
function findChildrenOverrides(instance) {
const children = toArray(instance.children())
children.forEach(c => {
overrides.push(c)
findChildrenOverrides(c)
})
}
overrides.forEach(findChildrenOverrides)

return overrides.map(o => {
const wrapped = Override.fromNative(o)
const property = overrideProperies[o.overridePoint().name()]
Object.defineProperty(wrapped, '__symbolMaster', {
writable: false,
enumerable: false,
value: this,
})
if (property && !o.isVisible) {
Object.defineProperty(wrapped, '__editable', {
writable: true,
enumerable: false,
value: property.canOverride,
})
}
return wrapped
})
},
set(overrides) {
overrides.forEach(o => {
const overridePoint = MSOverridePoint.alloc().init()
overridePoint.name = o.id
this._object.setOverridePoint_editable(overridePoint, o.editable)
})
},
})

SymbolMaster.extendObject('background', {
includedInInstance: {
get() {
Expand Down
1 change: 1 addition & 0 deletions Source/dom/layers/__tests__/SymbolInstance.test.js
Expand Up @@ -47,6 +47,7 @@ test('should have overrides', (context, document) => {
symbolOverride: false,
value: 'Test value',
isDefault: true,
editable: true,
affectedLayer: text.toJSON(),
}
delete result.affectedLayer.selected
Expand Down
40 changes: 40 additions & 0 deletions Source/dom/layers/__tests__/SymbolMaster.test.js
Expand Up @@ -75,6 +75,7 @@ test('should create a symbol master with a nested symbol', (context, document) =
symbolOverride: true,
value: nestedInstance.symbolId,
isDefault: true,
editable: true,
affectedLayer: nestedInstance.toJSON(),
}
delete result0.affectedLayer.overrides
Expand All @@ -88,6 +89,7 @@ test('should create a symbol master with a nested symbol', (context, document) =
symbolOverride: false,
value: 'Test value 2',
isDefault: true,
editable: true,
affectedLayer: text2.toJSON(),
}
delete result1.affectedLayer.overrides
Expand All @@ -101,6 +103,7 @@ test('should create a symbol master with a nested symbol', (context, document) =
symbolOverride: false,
value: 'Test value',
isDefault: true,
editable: true,
affectedLayer: text.toJSON(),
}
delete result2.affectedLayer.selected
Expand All @@ -110,6 +113,43 @@ test('should create a symbol master with a nested symbol', (context, document) =
expect(instance.overrides[2].toJSON()).toEqual(result2)
})

test('should have overrides', (context, document) => {
const { master, text } = createSymbolMaster(document)

expect(master.overrides.length).toBe(1)
const override = master.overrides[0]
const result = {
type: 'Override',
id: `${text.id}_stringValue`,
path: text.id,
property: 'stringValue',
symbolOverride: false,
value: 'Test value',
isDefault: true,
editable: true,
affectedLayer: text.toJSON(),
}
delete result.affectedLayer.selected
result.affectedLayer.style = master.overrides[0].affectedLayer.style.toJSON()
expect(override.toJSON()).toEqual(result)
})

test('should set overrides as editable or not', (context, document) => {
const { master } = createSymbolMaster(document)

expect(master.overrides[0].editable).toBe(true)
master.overrides[0].editable = false
expect(master.overrides[0].editable).toBe(false)

master.overrides = [
{
...master.overrides[0].toJSON(),
editable: true,
},
]
expect(master.overrides[0].editable).toBe(true)
})

test('should include `includedInInstance` in the `background`', (context, document) => {
// build the symbol master
const { master } = createSymbolMaster(document)
Expand Down
23 changes: 23 additions & 0 deletions Source/dom/models/Override.js
Expand Up @@ -55,6 +55,9 @@ Override.define('value', {
},
set(value) {
// __symbolInstance is set when building the Override
if (!this.__symbolInstance) {
throw new Error('Can only set `value` for a symbol instance')
}
this.__symbolInstance.setOverrideValue(this, value)
},
})
Expand All @@ -64,3 +67,23 @@ Override.define('isDefault', {
return !this._object.hasOverride()
},
})

Override.define('editable', {
get() {
if (typeof this.__editable !== 'undefined') {
return this.__editable
}
return Boolean(Number(this._object.isEditable()))
},
set(editable) {
// __symbolInstance is set when building the Override
if (!this.__symbolMaster) {
throw new Error('Can only set `editable` for a symbol master')
}
this.__editable = editable
this.__symbolMaster.sketchObject.setOverridePoint_editable(
this._object.overridePoint(),
editable
)
},
})
14 changes: 14 additions & 0 deletions Source/dom/models/__tests__/Override.test.js
Expand Up @@ -34,8 +34,11 @@ test('should be able to set overrides', (context, document) => {

expect(instance.overrides.length).toBe(1)
const override = instance.overrides[0]
expect(override.isDefault).toBe(true)
// check that an override can be logged
log(override)

// override
override.value = 'overridden'

expect(instance.overrides.length).toBe(1)
Expand All @@ -47,6 +50,7 @@ test('should be able to set overrides', (context, document) => {
symbolOverride: false,
value: 'overridden',
isDefault: false,
editable: true,
affectedLayer: text.toJSON(),
}
delete result.affectedLayer.selected
Expand Down Expand Up @@ -89,6 +93,7 @@ test('should change a nested symbol', (context, document) => {
symbolOverride: true,
value: nestedMaster2.symbolId,
isDefault: false,
editable: true,
}
delete result.affectedLayer.overrides
delete result.affectedLayer.selected
Expand Down Expand Up @@ -128,3 +133,12 @@ test('should handle image override', (context, document) => {
expect(instance.overrides[0].isDefault).toBe(false)
expect(instance.overrides[0].value.type).toBe('ImageData')
})

test('hidden layers should not be editable', (context, document) => {
const { master } = createSymbolMaster(document)
master.layers[0].hidden = true
const instance = master.createNewInstance()
document.selectedPage.layers = document.selectedPage.layers.concat(instance)

expect(instance.overrides[0].editable).toBe(false)
})
17 changes: 9 additions & 8 deletions docs/api/Override.md
Expand Up @@ -10,12 +10,13 @@ var overrides = symbolInstance.overrides

A [Symbol](https://www.sketchapp.com/docs/symbols/) override. This component is not exposed, it is only returned when accessing the `overrides` of a [Symbol Instance](#symbol-instance).

| Properties | |
| --------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| path<span class="arg-type">string</span> | The path to the override. It's formed by the symbolId of the nested symbols separated by a `/`. |
| Properties | |
| --------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| path<span class="arg-type">string</span> | The path to the override. It's formed by the symbolId of the nested symbols separated by a `/`. |
| property<span class="arg-type">string</span> | The property that this override controls. It can be `"stringValue"` for a text override, `"symbolID"` for a nested symbol, `"layerStyle"` for a shared layer style override, `"textStyle"` for a shared text style override, `"flowDestination"` for a Hotspot target override or `"image"` for an image override. |
| id<span class="arg-type">string</span> | The unique ID of the override (`${path}_${property}`). |
| symbolOverride<span class="arg-type">boolean</span> | If the override is a nested symbol override. |
| value<span class="arg-type">String / [ImageData](#imagedata)</span> | The value of the override which can be change. |
| isDefault<span class="arg-type">boolean</span> | If the override hasn't been changed and is the default value. |
| affectedLayer<span class="arg-type">[Text](#text) / [Image](#image) / [Symbol Instance](#symbolinstance)</span> | The layer the override applies to. It will be an immutable version of the layer |
| id<span class="arg-type">string</span> | The unique ID of the override (`${path}_${property}`). |
| symbolOverride<span class="arg-type">boolean</span> | If the override is a nested symbol override. |
| value<span class="arg-type">String / [ImageData](#imagedata)</span> | The value of the override which can be change. |
| isDefault<span class="arg-type">boolean</span> | If the override hasn't been changed and is the default value. |
| affectedLayer<span class="arg-type">[Text](#text) / [Image](#image) / [Symbol Instance](#symbolinstance)</span> | The layer the override applies to. It will be an immutable version of the layer |
| editable<span class="arg-type">boolean</span> | If the value of the override can be changed. |
1 change: 1 addition & 0 deletions docs/api/SymbolMaster.md
Expand Up @@ -24,6 +24,7 @@ A [Symbol](https://www.sketchapp.com/docs/symbols/) master. It is an instance of
| background.includedInInstance<span class="arg-type">boolean</span> | If the background should appear in the instances of the Symbol Master |
| background.color<span class="arg-type">string</span> | The rgba representation of the color of the background |
| symbolId<span class="arg-type">string</span> | The unique ID of the Symbol that the master and its instances share. |
| overrides<span class="arg-type">[Override](#symbol-override)[]</span> | The array of the overrides that the instances of the Symbol Master will be able to change. |

## Create a new Symbol Master

Expand Down