Skip to content

Commit

Permalink
Merge branch 'lineup-v4' into sgratzl/group_sort_influence
Browse files Browse the repository at this point in the history
  • Loading branch information
thinkh committed Apr 7, 2020
2 parents 0907e63 + bd46cbc commit d0f54fe
Show file tree
Hide file tree
Showing 7 changed files with 124 additions and 38 deletions.
53 changes: 53 additions & 0 deletions cypress/integration/pr289_color_mapping_reset.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import {withLineUp, waitReady, LineUpJSType, LineUp} from './utils/lineup';
import {generateData} from './utils/data';
import {openMoreDialog, closeDialog, resetDialog} from './utils/ui';

describe('pr275_sort_group', () => {
let lineup: LineUp;
let lineUpJS: LineUpJSType;
before(withLineUp((l, document) => {
lineUpJS = l;
const arr = generateData({
string: 0,
number: 1,
date: 0,
cat: 0
});
lineup = lineUpJS.asLineUp(document.body, arr);
waitReady(lineup);
}));

function openColorMappingDialog() {
return openMoreDialog('[data-type=number]', 'color-mapping');
}

it('reset color mapping', () => {
openColorMappingDialog().within(() => {
// select another color
cy.get('.lu-color-line:last .lu-checkbox-color:last').click();
});
closeDialog();

openColorMappingDialog().within(() => {
cy.get('.lu-color-line:last .lu-checkbox-color > input').last().should('be.checked');
resetDialog();
cy.get('.lu-color-line:first .lu-checkbox-color > input').first().should('be.checked');
});
closeDialog();
});

it('choose divergent color mapping', () => {
openColorMappingDialog().within(() => {
// select another color
cy.contains('Diverging Color').click();
cy.get('input[value="interpolateBrBG"]').check();
});
closeDialog();

openColorMappingDialog().within(() => {
cy.get('input[value="interpolateBrBG"]').should('be.checked');
resetDialog();
});
closeDialog();
});
});
12 changes: 10 additions & 2 deletions cypress/integration/utils/ui.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,19 @@ export function aggregateAll() {
}

export function closeDialog(action: 'cancel' | 'confirm' = 'confirm') {
cy.get(`.lu-dialog .lu-dialog-button[type=${action === 'cancel' ? 'button' : 'submit'}]`).click();
cy.get(`.lu-dialog-button[type=${action === 'cancel' ? 'button' : 'submit'}]`).click();
cy.get('.lu-backdrop').click();
}

export function openMoreDialog(column: string) {
export function resetDialog() {
cy.get(`.lu-dialog-button[type=reset]`).click();
}

export function openMoreDialog(column: string, action?: string) {
// open more menu
cy.get(`.le-th${column} .lu-action-more`).first().click();
if (action) {
cy.get(`.lu-more-options .lu-action-${action}`).click();
}
return cy.get('.lu-dialog').last().as('dialog');
}
4 changes: 4 additions & 0 deletions src/provider/ADataProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,10 @@ abstract class ADataProvider extends AEventDispatcher implements IDataProvider {
return factory;
}

getTypeFactory() {
return this.typeFactory;
}

/**
* events:
* * column changes: addColumn, removeColumn
Expand Down
4 changes: 3 additions & 1 deletion src/provider/interfaces.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {Column, Ranking, IColumnConstructor, IColumnDesc, IGroup, IndicesArray, IDataRow, IRankingDump, EAggregationState, IColorMappingFunctionConstructor, IMappingFunctionConstructor} from '../model';
import {Column, Ranking, IColumnConstructor, IColumnDesc, IGroup, IndicesArray, IDataRow, IRankingDump, EAggregationState, IColorMappingFunctionConstructor, IMappingFunctionConstructor, ITypeFactory} from '../model';
import {AEventDispatcher, ISequence} from '../internal';
import {IRenderTasks} from '../renderer';
import {IAbortAblePromise} from 'lineupengine';
Expand Down Expand Up @@ -39,6 +39,8 @@ export interface IDataProvider extends AEventDispatcher {

getTaskExecutor(): IRenderTasks;

getTypeFactory(): ITypeFactory;

takeSnapshot(col: Column): void;

selectAllOf(ranking: Ranking): void;
Expand Down
2 changes: 2 additions & 0 deletions src/styles/_dialogs.scss
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,8 @@

.#{$lu_css_prefix}-dialog-color {
width: 17em;
display: flex;
flex-direction: column;

> strong[data-toggle] {
cursor: pointer;
Expand Down
85 changes: 51 additions & 34 deletions src/ui/dialogs/ColorMappingDialog.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,39 +2,31 @@ import ADialog, {IDialogContext} from './ADialog';
import {schemeCategory10, schemeSet1, schemeSet2, schemeSet3, schemeAccent, schemeDark2, schemePastel2, schemePastel1} from 'd3-scale-chromatic';
import {round} from '../../internal';
import {uniqueId} from '../../renderer/utils';
import {QuantizedColorFunction, CustomColorMappingFunction, SolidColorFunction, SequentialColorFunction, DivergentColorFunction, DEFAULT_COLOR_FUNCTION} from '../../model/ColorMappingFunction';
import {IMapAbleColumn, DEFAULT_COLOR, IColorMappingFunction} from '../../model';
import {QuantizedColorFunction, CustomColorMappingFunction, SolidColorFunction, SequentialColorFunction, DivergentColorFunction} from '../../model/ColorMappingFunction';
import {IMapAbleColumn, DEFAULT_COLOR, IColorMappingFunction, IMapAbleDesc} from '../../model';
import {cssClass} from '../../styles';
import {IRankingHeaderContext} from '../interfaces';

export default class ColorMappingDialog extends ADialog {
private readonly before: IColorMappingFunction;
private readonly id = uniqueId('col');

constructor(private readonly column: IMapAbleColumn, dialog: IDialogContext) {
constructor(private readonly column: IMapAbleColumn, dialog: IDialogContext, private readonly ctx: IRankingHeaderContext) {
super(dialog, {
livePreview: 'colorMapping'
});

this.before = this.column.getColorMapping();
}

private createTemplate(id: string, current: IColorMappingFunction) {
private createTemplate(id: string, wrapped: IColorMappingFunction) {
const current = wrapped instanceof QuantizedColorFunction ? wrapped.base : wrapped;
const entries = current instanceof CustomColorMappingFunction ? current.entries : [];

let h = '';
h += `<datalist id="${id}L">${schemeCategory10.map((d) => `<option>${d}"</option>`).join('')}</datalist>`;
h += `<datalist id="${id}LW"><option>#FFFFFF"</option>${schemeCategory10.slice(0, -1).map((d) => `<option>${d}</option>`).join('')}</datalist>`;

h += `<strong>Quantization</strong>
<label class="${cssClass('checkbox')}">
<input name="kind" type="radio" id="${id}KC" value="continuous" ${!(current instanceof QuantizedColorFunction) ? 'checked' : ''}>
<span>Continuous</span>
</label>
<label class="${cssClass('checkbox')}">
<input name="kind" type="radio" id="${id}KQ" value="quantized" ${current instanceof QuantizedColorFunction ? 'checked' : ''}>
<span><input type="number" id="${id}KQS" min="2" step="1" value="${current instanceof QuantizedColorFunction ? current.steps : 5}">&nbsp; steps</span>
</label>`;

h += `<strong data-toggle="${current instanceof SolidColorFunction ? 'open' : ''}">Solid Color</strong>`;
h += `<div>`;
{
Expand All @@ -60,6 +52,14 @@ export default class ColorMappingDialog extends ADialog {

h += `<strong data-toggle="${current instanceof SequentialColorFunction || (current instanceof CustomColorMappingFunction && entries.length === 2) ? 'open' : ''}">Sequential Color</strong>`;
h += '<div>';
h += `<div><label class="${cssClass('checkbox')}">
<input name="kindS" type="radio" id="${id}KC_S" value="continuous" ${!(wrapped instanceof QuantizedColorFunction) ? 'checked' : ''}>
<span>Continuous</span>
</label>
<label class="${cssClass('checkbox')}">
<input name="kindS" type="radio" id="${id}KQ_S" value="quantized" ${wrapped instanceof QuantizedColorFunction ? 'checked' : ''}>
<span>Discrete&nbsp;<input type="number" id="${id}KQS_S" min="2" step="1" style="width: 3em" value="${wrapped instanceof QuantizedColorFunction ? `${wrapped.steps}"` : '5" disabled'}>&nbsp; steps</span>
</label></div>`;
{
const name = current instanceof SequentialColorFunction ? current.name : '';
for (const colors of Object.keys(SequentialColorFunction.FUNCTIONS)) {
Expand All @@ -79,8 +79,17 @@ export default class ColorMappingDialog extends ADialog {
h += '</div>';
h += `<strong data-toggle="${current instanceof DivergentColorFunction || (current instanceof CustomColorMappingFunction && entries.length === 3) ? 'open' : ''}">Diverging Color</strong>`;
h += '<div>';
h += `<div><label class="${cssClass('checkbox')}">
<input name="kindD" type="radio" id="${id}KC_D" value="continuous" ${!(wrapped instanceof QuantizedColorFunction) ? 'checked' : ''}>
<span>Continuous</span>
</label>
<label class="${cssClass('checkbox')}">
<input name="kindD" type="radio" id="${id}KQ_D" value="quantized" ${wrapped instanceof QuantizedColorFunction ? 'checked' : ''}>
<span>Discrete&nbsp;<input type="number" id="${id}KQS_D" min="2" step="1" style="width: 3em" value="${wrapped instanceof QuantizedColorFunction ? `${wrapped.steps}"` : '5" disabled'}>&nbsp; steps</span>
</label></div>`;

{
const name = current instanceof SequentialColorFunction ? current.name : '';
const name = current instanceof DivergentColorFunction ? current.name : '';
for (const colors of Object.keys(DivergentColorFunction.FUNCTIONS)) {
h += `<label class="${cssClass('checkbox')} ${cssClass('color-gradient')}"><input name="color" type="radio" value="${colors}" ${colors === name ? 'checked="checked"' : ''}>
<span data-c="${colors}" style="background: ${gradient(DivergentColorFunction.FUNCTIONS[colors], 11)}"></span>
Expand All @@ -106,12 +115,12 @@ export default class ColorMappingDialog extends ADialog {
if (!selected) {
return;
}
const quantized = this.findInput(`#${this.id}KQ`);
const steps = this.findInput(`#${this.id}KQS`);
const quantized = this.findInput(`#${this.id}KQ_S`);
const steps = this.findInput(`#${this.id}KQS_S`);

const base = toColor(selected, this.node);
if (quantized.checked && !(base instanceof SolidColorFunction)) {
this.column.setColorMapping(new QuantizedColorFunction(base, parseInt(steps.value, 10)));
this.column.setColorMapping(new QuantizedColorFunction(base, Number.parseInt(steps.value, 10)));
} else {
this.column.setColorMapping(base);
}
Expand All @@ -128,9 +137,6 @@ export default class ColorMappingDialog extends ADialog {
const id = this.id;
node.innerHTML = this.createTemplate(id, value);

const continuouos = this.findInput(`#${id}KC`);
const quantized = this.findInput(`#${id}KQ`);
const steps = this.findInput(`#${id}KQS`);
const toggles = <HTMLElement[]>Array.from(node.querySelectorAll('strong[data-toggle]'));

for (const toggle of toggles) {
Expand Down Expand Up @@ -179,30 +185,41 @@ export default class ColorMappingDialog extends ADialog {
};
});

continuouos.onchange = () => {
// sync and apply
const continuouos = this.findInput(`#${id}KC_S`);
const quantized = this.findInput(`#${id}KQ_S`);
const steps = this.findInput(`#${id}KQS_S`);
const continuouos2 = this.findInput(`#${id}KC_D`);
const quantized2 = this.findInput(`#${id}KQ_D`);
const steps2 = this.findInput(`#${id}KQS_D`);

continuouos.onchange = continuouos2.onchange = (evt) => {
continuouos.checked = continuouos2.checked = (<HTMLInputElement>evt.currentTarget).checked;
steps.disabled = steps2.disabled = !quantized.checked;
if (continuouos.checked) {
this.updateGradients(-1);
updateSelectedColor();
}
};
quantized.onchange = steps.onchange = () => {
if (!quantized.checked) {
this.updateGradients(-1);
steps.onchange = steps2.onchange = (evt) => {
steps.value = steps2.value = (<HTMLInputElement>evt.currentTarget).value;
this.updateGradients(Number.parseInt(steps.value, 10));
updateSelectedColor();
};
quantized.onchange = quantized2.onchange = (evt) => {
quantized.checked = quantized2.checked = (<HTMLInputElement>evt.currentTarget).checked;
steps.disabled = steps2.disabled = !quantized.checked;
if (quantized.checked) {
this.updateGradients(Number.parseInt(steps.value, 10));
updateSelectedColor();
return;
}
if (toggles[0].dataset.toggle === 'open') {
// auto open sequential
toggles[0].dataset.toggle = '';
toggles[1].dataset.toggle = 'open';
}
this.updateGradients(parseInt(steps.value, 10));
updateSelectedColor();
};
}

protected reset() {
this.render(this.node.querySelector<HTMLElement>(`.${cssClass('dialog-color')}`)!, DEFAULT_COLOR_FUNCTION);
const desc = <IMapAbleDesc>this.column.desc;
const colorMapping = this.ctx.provider.getTypeFactory().colorMappingFunction(desc.colorMapping || desc.color);
this.render(this.node.querySelector<HTMLElement>(`.${cssClass('dialog-color')}`)!, colorMapping);
}

protected submit() {
Expand Down
2 changes: 1 addition & 1 deletion src/ui/toolbar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -284,7 +284,7 @@ export const toolbarActions: {[key: string]: IToolbarAction} = {
filterCategorical: uiDialog('Filter &hellip;', CategoricalFilterDialog, () => [], {mode: 'menu+shortcut', featureCategory: 'ranking', featureLevel: 'basic'}),
filterOrdinal: uiDialog('Filter &hellip;', CategoricalMappingFilterDialog, () => [], {mode: 'menu+shortcut', featureCategory: 'ranking', featureLevel: 'basic'}),
filterBoolean: uiDialog('Filter &hellip;', BooleanFilterDialog, () => [], {mode: 'menu+shortcut', featureCategory: 'ranking', featureLevel: 'basic'}),
colorMapped: uiDialog('Color Mapping &hellip;', ColorMappingDialog, () => [], {mode: 'menu', featureCategory: 'ui', featureLevel: 'advanced'}),
colorMapped: uiDialog('Color Mapping &hellip;', ColorMappingDialog, (ctx) => [ctx], {mode: 'menu', featureCategory: 'ui', featureLevel: 'advanced'}),
colorMappedCategorical: uiDialog('Color Mapping &hellip;', CategoricalColorMappingDialog, () => [], {mode: 'menu', featureCategory: 'ui', featureLevel: 'advanced'}),
script: uiDialog('Edit Combine Script &hellip;', ScriptEditDialog, () => [], {mode: 'menu+shortcut', featureCategory: 'model', featureLevel: 'advanced'}),
reduce: uiDialog('Reduce by &hellip;', ReduceDialog, () => [], {featureCategory: 'model', featureLevel: 'advanced'}),
Expand Down

0 comments on commit d0f54fe

Please sign in to comment.