mirror of
https://github.com/gristlabs/grist-core.git
synced 2024-10-27 20:44:07 +00:00
Header colored (#581)
This commit is contained in:
parent
7c114bf600
commit
02841bd15c
@ -367,6 +367,9 @@
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.column_name .menu_toggle {
|
||||
z-index: 1;
|
||||
}
|
||||
/* Etc */
|
||||
|
||||
.g-column-main-menu {
|
||||
@ -408,11 +411,13 @@
|
||||
width: 13px;
|
||||
height: 13px;
|
||||
margin-right: 4px;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.g-column-label .kf_editable_label {
|
||||
padding-left: 1px;
|
||||
padding-right: 1px;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.g-column-label-spacer {
|
||||
|
@ -1073,8 +1073,28 @@ GridView.prototype.buildDom = function() {
|
||||
self.editingFormula() &&
|
||||
ko.unwrap(self.hoverColumn) === field._index()
|
||||
);
|
||||
|
||||
const headerTextColor = ko.computed(() => field.headerTextColor() || '');
|
||||
const headerFillColor = ko.computed(() => field.headerFillColor() || '');
|
||||
const headerFontBold = ko.computed(() => field.headerFontBold());
|
||||
const headerFontItalic = ko.computed(() => field.headerFontItalic());
|
||||
const headerFontUnderline = ko.computed(() => field.headerFontUnderline());
|
||||
const headerFontStrikethrough = ko.computed(() => field.headerFontStrikethrough());
|
||||
|
||||
return dom(
|
||||
'div.column_name.field',
|
||||
dom.autoDispose(headerTextColor),
|
||||
dom.autoDispose(headerFillColor),
|
||||
dom.autoDispose(headerFontBold),
|
||||
dom.autoDispose(headerFontItalic),
|
||||
dom.autoDispose(headerFontUnderline),
|
||||
dom.autoDispose(headerFontStrikethrough),
|
||||
kd.style('--grist-header-color', headerTextColor),
|
||||
kd.style('--grist-header-background-color', headerFillColor),
|
||||
kd.toggleClass('font-bold', headerFontBold),
|
||||
kd.toggleClass('font-italic', headerFontItalic),
|
||||
kd.toggleClass('font-underline', headerFontUnderline),
|
||||
kd.toggleClass('font-strikethrough', headerFontStrikethrough),
|
||||
kd.style('--frozen-position', () => ko.unwrap(this.frozenPositions.at(field._index()))),
|
||||
kd.toggleClass("frozen", () => ko.unwrap(this.frozenMap.at(field._index()))),
|
||||
kd.toggleClass("hover-column", isTooltip),
|
||||
|
@ -195,8 +195,11 @@
|
||||
}
|
||||
|
||||
.column_name {
|
||||
color: var(--grist-theme-table-header-fg, unset);
|
||||
background-color: var(--grist-theme-table-header-bg, var(--grist-color-light-grey));
|
||||
color: var(--grist-header-color,
|
||||
var(--grist-theme-table-header-fg), unset);
|
||||
background-color: var(--grist-header-background-color,
|
||||
var(--grist-theme-table-header-bg,
|
||||
var(--grist-color-light-grey)));
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
/* Column headers always show vertical gridlines, to make it clear how to resize them */
|
||||
@ -207,9 +210,11 @@
|
||||
border-left-color: var(--grist-theme-table-header-border, var(--grist-color-dark-grey));
|
||||
}
|
||||
|
||||
.column_name.selected {
|
||||
color: var(--grist-theme-table-header-selected-fg, unset);
|
||||
background-color: var(--grist-theme-table-header-selected-bg, var(--grist-color-medium-grey-opaque));
|
||||
.column_name.selected > .selection {
|
||||
background-color: var(--grist-theme-selection-header);
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.gridview_data_row_num.selected {
|
||||
|
@ -7,6 +7,15 @@ export interface Style {
|
||||
fontStrikethrough?: boolean|undefined;
|
||||
}
|
||||
|
||||
export interface HeaderStyle {
|
||||
headerTextColor?: string | undefined; // this can be string, undefined or an absent key.
|
||||
headerFillColor?: string | undefined;
|
||||
headerFontBold?: boolean | undefined;
|
||||
headerFontUnderline?: boolean | undefined;
|
||||
headerFontItalic?: boolean | undefined;
|
||||
headerFontStrikethrough?: boolean | undefined;
|
||||
}
|
||||
|
||||
export class CombinedStyle implements Style {
|
||||
public readonly textColor?: string;
|
||||
public readonly fillColor?: string;
|
||||
|
@ -17,6 +17,8 @@ export class ViewFieldConfig {
|
||||
public options: CommonOptions;
|
||||
/** Style options for a field or multiple fields */
|
||||
public style: ko.Computed<StyleOptions>;
|
||||
/** Header style options for a field or multiple fields */
|
||||
public headerStyle: ko.Computed<StyleOptions>;
|
||||
|
||||
// Rest of the options mimic the same options from ViewFieldRec.
|
||||
public wrap: modelUtil.KoSaveableObservable<boolean|undefined>;
|
||||
@ -255,6 +257,68 @@ export class ViewFieldConfig {
|
||||
result.revert = () => { zip(fields, state).forEach(([f, s]) => f!.style(s!)); };
|
||||
return result;
|
||||
});
|
||||
|
||||
this.headerStyle = ko.pureComputed(() => {
|
||||
const fields = this.fields();
|
||||
const multiSelect = fields.length > 1;
|
||||
const savableOptions = modelUtil.savingComputed({
|
||||
read: () => {
|
||||
// For one column, just proxy this to the field.
|
||||
if (!multiSelect) {
|
||||
return this._field.widgetOptionsJson();
|
||||
}
|
||||
// Assemble final json object.
|
||||
const result: any = {};
|
||||
// First get all widgetOption jsons from all columns/fields.
|
||||
const optionList = fields.map(f => f.widgetOptionsJson());
|
||||
// And fill only those that are common
|
||||
for(const key of ['headerTextColor', 'headerFillColor', 'headerFontBold',
|
||||
'headerFontItalic', 'headerFontUnderline', 'headerFontStrikethrough']) {
|
||||
// Setting null means that this options is there, but has no value.
|
||||
result[key] = null;
|
||||
// If all columns have the same value, use it.
|
||||
if (allSame(optionList.map(v => v[key]))) {
|
||||
result[key] = optionList[0][key] ?? null;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
},
|
||||
write: (setter, value) => {
|
||||
if (!multiSelect) {
|
||||
return setter(this._field.widgetOptionsJson, value);
|
||||
}
|
||||
// When the creator panel is saving widgetOptions, it will pass
|
||||
// our virtual widgetObject, which has nulls for mixed values.
|
||||
// If this option wasn't changed (set), we don't want to save it.
|
||||
value = {...value};
|
||||
for(const key of Object.keys(value)) {
|
||||
if (value[key] === null) {
|
||||
delete value[key];
|
||||
}
|
||||
}
|
||||
// Now update all options, for all fields, by amending the options
|
||||
// object from the field/column.
|
||||
for(const item of fields) {
|
||||
const previous = item.widgetOptionsJson.peek();
|
||||
setter(item.widgetOptionsJson, {
|
||||
...previous,
|
||||
...value,
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
// Style picker needs to be able revert to previous value, if user cancels.
|
||||
const state = fields.map(f => f.headerStyle.peek());
|
||||
// We need some additional information about each property.
|
||||
const result: StyleOptions = extendObservable(modelUtil.objObservable(savableOptions), {
|
||||
// Property has mixed value, if not all options are the same.
|
||||
mixed: prop => ko.pureComputed(() => !allSame(fields.map(f => f.widgetOptionsJson.prop(prop)()))),
|
||||
// Property has empty value, if all options are empty (are null, undefined, empty Array or empty Object).
|
||||
empty: prop => ko.pureComputed(() => allEmpty(fields.map(f => f.widgetOptionsJson.prop(prop)()))),
|
||||
});
|
||||
result.revert = () => { zip(fields, state).forEach(([f, s]) => f!.headerStyle(s!)); };
|
||||
return result;
|
||||
});
|
||||
}
|
||||
|
||||
// Helper for Choice/ChoiceList columns, that saves widget options and renames values in a document
|
||||
|
@ -2,7 +2,7 @@ import {ColumnRec, DocModel, IRowModel, refListRecords, refRecord, ViewSectionRe
|
||||
import {formatterForRec} from 'app/client/models/entities/ColumnRec';
|
||||
import * as modelUtil from 'app/client/models/modelUtil';
|
||||
import {removeRule, RuleOwner} from 'app/client/models/RuleOwner';
|
||||
import {Style} from 'app/client/models/Styles';
|
||||
import { HeaderStyle, Style } from 'app/client/models/Styles';
|
||||
import {ViewFieldConfig} from 'app/client/models/ViewFieldConfig';
|
||||
import * as UserType from 'app/client/widgets/UserType';
|
||||
import {DocumentSettings} from 'app/common/DocumentSettings';
|
||||
@ -76,8 +76,15 @@ export interface ViewFieldRec extends IRowModel<"_grist_Views_section_field">, R
|
||||
fontUnderline: modelUtil.KoSaveableObservable<boolean|undefined>;
|
||||
fontItalic: modelUtil.KoSaveableObservable<boolean|undefined>;
|
||||
fontStrikethrough: modelUtil.KoSaveableObservable<boolean|undefined>;
|
||||
// Helper computed to change style of a cell without saving it.
|
||||
headerTextColor: modelUtil.KoSaveableObservable<string|undefined>;
|
||||
headerFillColor: modelUtil.KoSaveableObservable<string|undefined>;
|
||||
headerFontBold: modelUtil.KoSaveableObservable<boolean|undefined>;
|
||||
headerFontUnderline: modelUtil.KoSaveableObservable<boolean|undefined>;
|
||||
headerFontItalic: modelUtil.KoSaveableObservable<boolean|undefined>;
|
||||
headerFontStrikethrough: modelUtil.KoSaveableObservable<boolean|undefined>;
|
||||
// Helper computed to change style of a cell and headerStyle without saving it.
|
||||
style: ko.PureComputed<Style>;
|
||||
headerStyle: ko.PureComputed<HeaderStyle>;
|
||||
|
||||
config: ViewFieldConfig;
|
||||
|
||||
@ -236,6 +243,12 @@ export function createViewFieldRec(this: ViewFieldRec, docModel: DocModel): void
|
||||
this.fontUnderline = this.widgetOptionsJson.prop('fontUnderline');
|
||||
this.fontItalic = this.widgetOptionsJson.prop('fontItalic');
|
||||
this.fontStrikethrough = this.widgetOptionsJson.prop('fontStrikethrough');
|
||||
this.headerTextColor = this.widgetOptionsJson.prop('headerTextColor');
|
||||
this.headerFillColor = this.widgetOptionsJson.prop('headerFillColor');
|
||||
this.headerFontBold = this.widgetOptionsJson.prop('headerFontBold');
|
||||
this.headerFontUnderline = this.widgetOptionsJson.prop('headerFontUnderline');
|
||||
this.headerFontItalic = this.widgetOptionsJson.prop('headerFontItalic');
|
||||
this.headerFontStrikethrough = this.widgetOptionsJson.prop('headerFontStrikethrough');
|
||||
|
||||
this.documentSettings = ko.pureComputed(() => docModel.docInfoRow.documentSettingsJson());
|
||||
this.style = ko.pureComputed({
|
||||
@ -251,6 +264,19 @@ export function createViewFieldRec(this: ViewFieldRec, docModel: DocModel): void
|
||||
this.widgetOptionsJson.update(style);
|
||||
},
|
||||
});
|
||||
this.headerStyle = ko.pureComputed({
|
||||
read: () => ({
|
||||
headerTextColor: this.headerTextColor(),
|
||||
headerFillColor: this.headerFillColor(),
|
||||
headerFontBold: this.headerFontBold(),
|
||||
headerFontUnderline: this.headerFontUnderline(),
|
||||
headerFontItalic: this.headerFontItalic(),
|
||||
headerFontStrikethrough: this.headerFontStrikethrough(),
|
||||
}) as HeaderStyle,
|
||||
write: (headerStyle: HeaderStyle) => {
|
||||
this.widgetOptionsJson.update(headerStyle);
|
||||
},
|
||||
});
|
||||
|
||||
this.tableId = ko.pureComputed(() => this.column().table().tableId());
|
||||
this.rulesList = ko.pureComputed(() => this._fieldOrColumn().rules());
|
||||
|
@ -344,6 +344,7 @@ export const theme = {
|
||||
colors.selectionOpaque),
|
||||
selectionOpaqueDarkBg: new CustomProp('theme-selection-opaque-dark-bg', undefined,
|
||||
colors.selectionDarkerOpaque),
|
||||
selectionHeader: new CustomProp('theme-selection-header', undefined, colors.mediumGrey),
|
||||
|
||||
/* Widgets */
|
||||
widgetBg: new CustomProp('theme-widget-bg', undefined, 'white'),
|
||||
|
@ -4,7 +4,7 @@ import {GristDoc} from 'app/client/components/GristDoc';
|
||||
import {ViewFieldRec} from 'app/client/models/entities/ViewFieldRec';
|
||||
import {textButton} from 'app/client/ui2018/buttons';
|
||||
import {ColorOption, colorSelect} from 'app/client/ui2018/ColorSelect';
|
||||
import {theme, vars} from 'app/client/ui2018/cssVars';
|
||||
import {testId, theme, vars} from 'app/client/ui2018/cssVars';
|
||||
import {ConditionalStyle} from 'app/client/widgets/ConditionalStyle';
|
||||
import {Computed, Disposable, dom, DomContents, fromKo, styled} from 'grainjs';
|
||||
|
||||
@ -21,12 +21,61 @@ export class CellStyle extends Disposable {
|
||||
}
|
||||
|
||||
public buildDom(): DomContents {
|
||||
const isTableWidget = this._field.viewSection().parentKey() === 'record';
|
||||
return [
|
||||
dom.maybe(use => isTableWidget, () => {
|
||||
return [
|
||||
cssLine(
|
||||
cssLabel(t('HEADER STYLE')),
|
||||
),
|
||||
cssRow(
|
||||
testId('header-color-select'),
|
||||
dom.domComputedOwned(fromKo(this._field.config.headerStyle), (holder, options) => {
|
||||
const headerTextColor = fromKo(options.prop("headerTextColor"));
|
||||
const headerFillColor = fromKo(options.prop("headerFillColor"));
|
||||
const headerFontBold = fromKo(options.prop("headerFontBold"));
|
||||
const headerFontUnderline = fromKo(options.prop("headerFontUnderline"));
|
||||
const headerFontItalic = fromKo(options.prop("headerFontItalic"));
|
||||
const headerFontStrikethrough = fromKo(options.prop("headerFontStrikethrough"));
|
||||
const hasMixedStyle = Computed.create(holder, use => {
|
||||
if (!use(this._field.config.multiselect)) { return false; }
|
||||
const commonStyle = [
|
||||
use(options.mixed('headerTextColor')),
|
||||
use(options.mixed('headerFillColor')),
|
||||
use(options.mixed('headerFontBold')),
|
||||
use(options.mixed('headerFontUnderline')),
|
||||
use(options.mixed('headerFontItalic')),
|
||||
use(options.mixed('headerFontStrikethrough'))
|
||||
];
|
||||
return commonStyle.some(Boolean);
|
||||
});
|
||||
return colorSelect(
|
||||
{
|
||||
textColor: new ColorOption(
|
||||
{ color: headerTextColor, defaultColor: this._defaultTextColor, noneText: 'default' }
|
||||
),
|
||||
fillColor: new ColorOption(
|
||||
{ color: headerFillColor, allowsNone: true, noneText: 'none' }
|
||||
),
|
||||
fontBold: headerFontBold,
|
||||
fontItalic: headerFontItalic,
|
||||
fontUnderline: headerFontUnderline,
|
||||
fontStrikethrough: headerFontStrikethrough
|
||||
}, {
|
||||
onSave: () => options.save(),
|
||||
onRevert: () => options.revert(),
|
||||
placeholder: use => use(hasMixedStyle) ? t('Mixed style') : t('Default header style')
|
||||
}
|
||||
);
|
||||
}),
|
||||
)];
|
||||
}),
|
||||
cssLine(
|
||||
cssLabel(t('CELL STYLE')),
|
||||
cssButton(t('Open row styles'), dom.on('click', allCommands.viewTabOpen.run)),
|
||||
),
|
||||
cssRow(
|
||||
testId('cell-color-select'),
|
||||
dom.domComputedOwned(fromKo(this._field.config.style), (holder, options) => {
|
||||
const textColor = fromKo(options.prop("textColor"));
|
||||
const fillColor = fromKo(options.prop("fillColor"));
|
||||
|
@ -173,6 +173,7 @@ export interface ThemeColors {
|
||||
'selection-opaque-fg': string;
|
||||
'selection-opaque-bg': string;
|
||||
'selection-opaque-dark-bg': string;
|
||||
'selection-header': string;
|
||||
|
||||
/* Widgets */
|
||||
'widget-bg': string;
|
||||
|
@ -152,6 +152,7 @@ export const GristDark: ThemeColors = {
|
||||
'selection-opaque-fg': 'white',
|
||||
'selection-opaque-bg': '#2F4748',
|
||||
'selection-opaque-dark-bg': '#253E3E',
|
||||
'selection-header': 'rgba(107,107,144,0.4)',
|
||||
|
||||
/* Widgets */
|
||||
'widget-bg': '#32323F',
|
||||
|
@ -152,6 +152,7 @@ export const GristLight: ThemeColors = {
|
||||
'selection-opaque-fg': 'black',
|
||||
'selection-opaque-bg': '#DCF4EB',
|
||||
'selection-opaque-dark-bg': '#D6EEE5',
|
||||
'selection-header': 'rgba(217,217,217,0.6)',
|
||||
|
||||
/* Widgets */
|
||||
'widget-bg': 'white',
|
||||
|
@ -851,7 +851,9 @@
|
||||
"Cell Style": "Cell Style",
|
||||
"Default cell style": "Default cell style",
|
||||
"Mixed style": "Mixed style",
|
||||
"Open row styles": "Open row styles"
|
||||
"Open row styles": "Open row styles",
|
||||
"Default header style": "Default header style",
|
||||
"Header Style": "Header Style"
|
||||
},
|
||||
"ChoiceTextBox": {
|
||||
"CHOICES": "CHOICES"
|
||||
|
@ -23,7 +23,7 @@ describe('CellColor', function() {
|
||||
it('should save by clicking away', async function() {
|
||||
await gu.getCell('A', 1).click();
|
||||
// open color picker
|
||||
await gu.openColorPicker();
|
||||
await gu.openCellColorPicker();
|
||||
await gu.setFillColor('red');
|
||||
await gu.setTextColor('blue');
|
||||
// Save by clicking B column
|
||||
@ -46,7 +46,7 @@ describe('CellColor', function() {
|
||||
it('should undo and redo colors when clicked away', async function() {
|
||||
await gu.getCell('A', 1).click();
|
||||
// open color picker
|
||||
await gu.openColorPicker();
|
||||
await gu.openCellColorPicker();
|
||||
await gu.setFillColor('red');
|
||||
await gu.setTextColor('blue');
|
||||
// Save by clicking B column
|
||||
@ -78,7 +78,7 @@ describe('CellColor', function() {
|
||||
|
||||
await cell.click();
|
||||
// open color picker
|
||||
await driver.find('.test-color-select').click();
|
||||
await gu.openCellColorPicker();
|
||||
|
||||
// set cell colors
|
||||
await gu.setColor(driver.find('.test-text-input'), 'rgb(0, 255, 0)');
|
||||
@ -247,7 +247,7 @@ describe('CellColor', function() {
|
||||
assert.equal(await cell.matches('.test-attachment-widget'), true);
|
||||
|
||||
// open color picker
|
||||
await driver.find('.test-color-select').click();
|
||||
await gu.openCellColorPicker();
|
||||
|
||||
// set and check cellColor
|
||||
await gu.setColor(driver.find('.test-text-input'), 'rgb(0, 255, 0)');
|
||||
@ -277,7 +277,7 @@ describe('CellColor', function() {
|
||||
assert.equal(await cell.find('.widget_checkbox').isPresent(), true);
|
||||
|
||||
// open color picker
|
||||
await driver.find('.test-color-select').click();
|
||||
await gu.openCellColorPicker();
|
||||
|
||||
// set and check cell color
|
||||
await gu.setColor(driver.find('.test-text-input'), 'rgb(0, 255, 0)');
|
||||
@ -311,7 +311,7 @@ describe('CellColor', function() {
|
||||
const clip = cell.find('.field_clip');
|
||||
|
||||
// open color picker
|
||||
await driver.find('.test-color-select').click();
|
||||
await gu.openCellColorPicker();
|
||||
|
||||
// set and check cell color
|
||||
await gu.setColor(driver.find('.test-text-input'), 'rgb(0, 255, 0)');
|
||||
@ -338,7 +338,7 @@ describe('CellColor', function() {
|
||||
const cell = gu.getCell('A', 1).find('.field_clip');
|
||||
|
||||
// open color picker
|
||||
await driver.find('.test-color-select').click();
|
||||
await gu.openCellColorPicker();
|
||||
|
||||
// set and check cell color
|
||||
await gu.setColor(driver.find('.test-text-input'), 'rgb(0, 255, 0)');
|
||||
@ -369,7 +369,7 @@ describe('CellColor', function() {
|
||||
await gu.enterCell(Key.DELETE);
|
||||
|
||||
// open color picker
|
||||
await driver.find('.test-color-select').click();
|
||||
await gu.openCellColorPicker();
|
||||
|
||||
// set and check cell color
|
||||
await gu.setColor(driver.find('.test-text-input'), 'rgb(0, 255, 0)');
|
||||
@ -400,7 +400,7 @@ describe('CellColor', function() {
|
||||
await gu.enterCell('foo');
|
||||
|
||||
// open color picker
|
||||
await driver.find('.test-color-select').click();
|
||||
await gu.openCellColorPicker();
|
||||
|
||||
// set and check cell color
|
||||
const cell = await gu.getCell('A', 1).find('.field_clip');
|
||||
@ -430,7 +430,7 @@ describe('CellColor', function() {
|
||||
let cell = gu.getCell('A', 1).find('.field_clip');
|
||||
|
||||
// open color picker
|
||||
await driver.find('.test-color-select').click();
|
||||
await gu.openCellColorPicker();
|
||||
|
||||
// set and check cell color
|
||||
await gu.setColor(driver.find('.test-text-input'), 'rgb(0, 255, 0)');
|
||||
@ -465,7 +465,7 @@ describe('CellColor', function() {
|
||||
await gu.setType(/Toggle/);
|
||||
|
||||
// open the color picker
|
||||
await driver.find('.test-color-select').click();
|
||||
await gu.openCellColorPicker();
|
||||
|
||||
// check color preview is correct
|
||||
assert.equal(await driver.find('.test-text-hex').value(), '#606060');
|
||||
@ -479,7 +479,7 @@ describe('CellColor', function() {
|
||||
await gu.waitForServer();
|
||||
|
||||
// open the color picker
|
||||
await driver.find('.test-color-select').click();
|
||||
await gu.openCellColorPicker();
|
||||
|
||||
// check color preview is correct
|
||||
assert.equal(await driver.find('.test-text-hex').value(), '#2CB0AF');
|
||||
@ -505,7 +505,7 @@ describe('CellColor', function() {
|
||||
assert.equal(await cell().find('.checkmark_stem').getCssValue('background-color'), gu.hexToRgb('#606060'));
|
||||
|
||||
// open the color picker and press ESC
|
||||
await driver.find('.test-color-select').click();
|
||||
await gu.openCellColorPicker();
|
||||
await driver.wait(() => driver.find('.test-fill-palette').isPresent(), 3000);
|
||||
await driver.sendKeys(Key.ESCAPE);
|
||||
await driver.wait(async () => !(await driver.find('.test-fill-palette').isPresent()), 3000);
|
||||
|
112
test/nbrowser/HeaderColor.ts
Normal file
112
test/nbrowser/HeaderColor.ts
Normal file
@ -0,0 +1,112 @@
|
||||
import { assert, driver, Key } from 'mocha-webdriver';
|
||||
import * as gu from 'test/nbrowser/gristUtils';
|
||||
import { setupTestSuite } from 'test/nbrowser/testUtils';
|
||||
|
||||
const defaultHeaderBackgroundColor = '#f7f7f7';
|
||||
|
||||
describe('HeaderColor', function () {
|
||||
this.timeout(20000);
|
||||
const cleanup = setupTestSuite();
|
||||
|
||||
before(async () => {
|
||||
// Create a new document
|
||||
const mainSession = await gu.session().login();
|
||||
await mainSession.tempNewDoc(cleanup, 'HeaderColor');
|
||||
// add records
|
||||
await gu.enterCell('a');
|
||||
await gu.enterCell('b');
|
||||
await gu.enterCell('c');
|
||||
await gu.toggleSidePanel('right', 'open');
|
||||
await driver.find('.test-right-tab-field').click();
|
||||
});
|
||||
|
||||
it('should save by clicking away', async function () {
|
||||
await gu.getCell('A', 1).click();
|
||||
// open color picker
|
||||
await gu.openHeaderColorPicker();
|
||||
await gu.setFillColor('red');
|
||||
await gu.setTextColor('blue');
|
||||
// Save by clicking B column
|
||||
await gu.getCell('B', 1).click();
|
||||
await gu.waitForServer();
|
||||
// Make sure the color is saved.
|
||||
await gu.assertHeaderFillColor('A', 'red');
|
||||
await gu.assertHeaderTextColor('A', 'blue');
|
||||
await gu.assertHeaderFillColor('B', defaultHeaderBackgroundColor);
|
||||
await gu.assertHeaderTextColor('B', 'black');
|
||||
// Make sure it sticks after reload.
|
||||
await driver.navigate().refresh();
|
||||
await gu.waitForDocToLoad();
|
||||
await gu.assertHeaderFillColor('A', 'red');
|
||||
await gu.assertHeaderTextColor('A', 'blue');
|
||||
await gu.assertHeaderFillColor('B', defaultHeaderBackgroundColor);
|
||||
await gu.assertHeaderTextColor('B', 'black');
|
||||
});
|
||||
|
||||
it('should undo and redo colors when clicked away', async function () {
|
||||
await gu.getCell('A', 1).click();
|
||||
// open color picker
|
||||
await gu.openHeaderColorPicker();
|
||||
await gu.setFillColor('red');
|
||||
await gu.setTextColor('blue');
|
||||
// Save by clicking B column
|
||||
await gu.getCell('B', 1).click();
|
||||
await gu.waitForServer();
|
||||
// Make sure the color is saved.
|
||||
await gu.assertHeaderFillColor('A', 'red');
|
||||
await gu.assertHeaderTextColor('A', 'blue');
|
||||
await gu.assertHeaderFillColor('B', defaultHeaderBackgroundColor);
|
||||
await gu.assertHeaderTextColor('B', 'black');
|
||||
// Make sure then undoing works.
|
||||
await gu.undo();
|
||||
await gu.assertHeaderFillColor('A', defaultHeaderBackgroundColor);
|
||||
await gu.assertHeaderTextColor('A', 'black');
|
||||
await gu.assertHeaderFillColor('B', defaultHeaderBackgroundColor);
|
||||
await gu.assertHeaderTextColor('B', 'black');
|
||||
await gu.redo();
|
||||
await gu.assertHeaderFillColor('A', 'red');
|
||||
await gu.assertHeaderTextColor('A', 'blue');
|
||||
await gu.assertHeaderFillColor('B', defaultHeaderBackgroundColor);
|
||||
await gu.assertHeaderTextColor('B', 'black');
|
||||
});
|
||||
|
||||
|
||||
it('should work correctly on Grid view', async function () {
|
||||
const columnHeader = gu.getColumnHeader('C');
|
||||
|
||||
await columnHeader.click();
|
||||
// open color picker
|
||||
await gu.openHeaderColorPicker();
|
||||
|
||||
// set header colors
|
||||
await gu.setColor(driver.find('.test-text-input'), 'rgb(255, 0, 0)');
|
||||
await gu.setColor(driver.find('.test-fill-input'), 'rgb(0, 0, 255)');
|
||||
|
||||
// press enter to close color picker
|
||||
await driver.sendKeys(Key.ENTER);
|
||||
|
||||
// check header colors
|
||||
assert.equal(await columnHeader.getCssValue('color'), 'rgba(255, 0, 0, 1)');
|
||||
assert.equal(await columnHeader.getCssValue('background-color'), 'rgba(0, 0, 255, 1)');
|
||||
});
|
||||
|
||||
it('should not exist in Detail view', async function () {
|
||||
// Color the A column in Grid View
|
||||
const columnHeader = gu.getColumnHeader('A');
|
||||
await columnHeader.click();
|
||||
await gu.openHeaderColorPicker();
|
||||
await gu.setColor(driver.find('.test-text-input'), 'rgb(255, 0, 0)');
|
||||
await driver.sendKeys(Key.ENTER);
|
||||
|
||||
// Add a card list widget of Table1
|
||||
await gu.addNewSection(/Card List/, /Table1/);
|
||||
|
||||
// check header colors
|
||||
const detailHeader = await driver.findContent('.g_record_detail_label', gu.exactMatch('A'));
|
||||
assert.equal(await detailHeader.getCssValue('color'), 'rgba(146, 146, 153, 1)');
|
||||
|
||||
// There is no header color picker
|
||||
assert.isFalse(await driver.find('.test-header-color-select .test-color-select').isPresent());
|
||||
|
||||
});
|
||||
});
|
@ -86,7 +86,7 @@ describe('MultiColumn', function() {
|
||||
await gu.getCell('Test2', 3).click();
|
||||
await gu.enterCell('Table1', Key.ENTER);
|
||||
await selectColumns('Test1', 'Test2');
|
||||
await gu.openColorPicker();
|
||||
await gu.openCellColorPicker();
|
||||
await gu.setFillColor(blue);
|
||||
// Clicking on one of the cell caused that the color was not saved.
|
||||
await gu.getCell('Test2', 1).click();
|
||||
@ -346,21 +346,21 @@ describe('MultiColumn', function() {
|
||||
await removeColumn('Test1');
|
||||
await removeColumn('Test2');
|
||||
});
|
||||
it('should change background for multiple columns', async () => {
|
||||
it('should change cell background for multiple columns', async () => {
|
||||
await selectColumns('Test1', 'Test2');
|
||||
assert.equal(await colorLabel(), "Default cell style");
|
||||
await gu.openColorPicker();
|
||||
assert.equal(await cellColorLabel(), "Default cell style");
|
||||
await gu.openCellColorPicker();
|
||||
await gu.setFillColor(blue);
|
||||
await gu.assertFillColor(await gu.getCell('Test1', 1).find(".field_clip"), blue);
|
||||
await gu.assertFillColor(await gu.getCell('Test2', 1).find(".field_clip"), blue);
|
||||
await driver.sendKeys(Key.ESCAPE);
|
||||
await gu.assertFillColor(await gu.getCell('Test1', 1).find(".field_clip"), transparent);
|
||||
await gu.assertFillColor(await gu.getCell('Test2', 1).find(".field_clip"), transparent);
|
||||
assert.equal(await colorLabel(), "Default cell style");
|
||||
assert.equal(await cellColorLabel(), "Default cell style");
|
||||
|
||||
// Change one cell to red
|
||||
await selectColumns('Test1');
|
||||
await gu.openColorPicker();
|
||||
await gu.openCellColorPicker();
|
||||
await gu.setFillColor(red);
|
||||
await driver.sendKeys(Key.ENTER);
|
||||
await gu.waitForServer();
|
||||
@ -369,9 +369,9 @@ describe('MultiColumn', function() {
|
||||
|
||||
// Check label and colors for multicolumn selection.
|
||||
await selectColumns('Test1', 'Test2');
|
||||
assert.equal(await colorLabel(), "Mixed style");
|
||||
assert.equal(await cellColorLabel(), "Mixed style");
|
||||
// Try to change to blue, but press escape.
|
||||
await gu.openColorPicker();
|
||||
await gu.openCellColorPicker();
|
||||
await gu.setFillColor(blue);
|
||||
await gu.assertFillColor(await gu.getCell('Test1', 1).find(".field_clip"), blue);
|
||||
await gu.assertFillColor(await gu.getCell('Test2', 1).find(".field_clip"), blue);
|
||||
@ -381,21 +381,73 @@ describe('MultiColumn', function() {
|
||||
await gu.assertFillColor(await gu.getCell('Test2', 1).find(".field_clip"), transparent);
|
||||
|
||||
// Change both colors.
|
||||
await gu.openColorPicker();
|
||||
await gu.openCellColorPicker();
|
||||
await gu.setFillColor(blue);
|
||||
await driver.sendKeys(Key.ENTER);
|
||||
await gu.waitForServer();
|
||||
assert.equal(await colorLabel(), "Default cell style");
|
||||
assert.equal(await cellColorLabel(), "Default cell style");
|
||||
await gu.assertFillColor(await gu.getCell('Test1', 1).find(".field_clip"), blue);
|
||||
await gu.assertFillColor(await gu.getCell('Test2', 1).find(".field_clip"), blue);
|
||||
|
||||
// Make sure they stick.
|
||||
await driver.navigate().refresh();
|
||||
await gu.waitForDocToLoad();
|
||||
assert.equal(await colorLabel(), "Default cell style");
|
||||
assert.equal(await cellColorLabel(), "Default cell style");
|
||||
await gu.assertFillColor(await gu.getCell('Test1', 1).find(".field_clip"), blue);
|
||||
await gu.assertFillColor(await gu.getCell('Test2', 1).find(".field_clip"), blue);
|
||||
});
|
||||
|
||||
it('should change header background for multiple columns', async () => {
|
||||
const defaultHeaderFillColor = 'rgba(247, 247, 247, 1)';
|
||||
await selectColumns('Test1', 'Test2');
|
||||
assert.equal(await headerColorLabel(), "Default header style");
|
||||
await gu.openHeaderColorPicker();
|
||||
await gu.setFillColor(blue);
|
||||
await gu.assertHeaderFillColor('Test1', blue);
|
||||
await gu.assertHeaderFillColor('Test2', blue);
|
||||
await driver.sendKeys(Key.ESCAPE);
|
||||
await gu.assertHeaderFillColor('Test1', defaultHeaderFillColor);
|
||||
await gu.assertHeaderFillColor('Test2', defaultHeaderFillColor);
|
||||
assert.equal(await headerColorLabel(), "Default header style");
|
||||
|
||||
// Change one header to red
|
||||
await selectColumns('Test1');
|
||||
await gu.openHeaderColorPicker();
|
||||
await gu.setFillColor(red);
|
||||
await driver.sendKeys(Key.ENTER);
|
||||
await gu.waitForServer();
|
||||
await gu.assertHeaderFillColor('Test1', red);
|
||||
await gu.assertHeaderFillColor('Test2', defaultHeaderFillColor);
|
||||
|
||||
// Check label and colors for multicolumn selection.
|
||||
await selectColumns('Test1', 'Test2');
|
||||
assert.equal(await headerColorLabel(), "Mixed style");
|
||||
// Try to change to blue, but press escape.
|
||||
await gu.openHeaderColorPicker();
|
||||
await gu.setFillColor(blue);
|
||||
await gu.assertHeaderFillColor('Test1', blue);
|
||||
await gu.assertHeaderFillColor('Test2', blue);
|
||||
await driver.sendKeys(Key.ESCAPE);
|
||||
|
||||
await gu.assertHeaderFillColor('Test1', red);
|
||||
await gu.assertHeaderFillColor('Test2', defaultHeaderFillColor);
|
||||
|
||||
// Change both colors.
|
||||
await gu.openHeaderColorPicker();
|
||||
await gu.setFillColor(blue);
|
||||
await driver.sendKeys(Key.ENTER);
|
||||
await gu.waitForServer();
|
||||
assert.equal(await headerColorLabel(), "Default header style");
|
||||
await gu.assertHeaderFillColor('Test1', blue);
|
||||
await gu.assertHeaderFillColor('Test2', blue);
|
||||
|
||||
// Make sure they stick.
|
||||
await driver.navigate().refresh();
|
||||
await gu.waitForDocToLoad();
|
||||
assert.equal(await headerColorLabel(), "Default header style");
|
||||
await gu.assertHeaderFillColor('Test1', blue);
|
||||
await gu.assertHeaderFillColor('Test2', blue);
|
||||
});
|
||||
});
|
||||
|
||||
describe(`test for Integer column`, function() {
|
||||
@ -1344,8 +1396,14 @@ async function slider(value?: number) {
|
||||
return parseInt(await driver.find(".test-pw-thumbnail-size").getAttribute('value'));
|
||||
}
|
||||
|
||||
async function colorLabel() {
|
||||
async function cellColorLabel() {
|
||||
// Text actually contains T symbol before.
|
||||
const label = await driver.find(".test-color-select").getText();
|
||||
const label = await driver.find(".test-cell-color-select .test-color-select").getText();
|
||||
return label.replace(/^T/, '').trim();
|
||||
}
|
||||
|
||||
async function headerColorLabel() {
|
||||
// Text actually contains T symbol before.
|
||||
const label = await driver.find(".test-header-color-select .test-color-select").getText();
|
||||
return label.replace(/^T/, '').trim();
|
||||
}
|
||||
|
@ -2203,15 +2203,30 @@ export async function clickAway() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens a color picker, either the default one or the one for a specific style rule.
|
||||
* Opens the header color picker.
|
||||
*/
|
||||
export function openColorPicker(nr?: number) {
|
||||
export function openHeaderColorPicker() {
|
||||
return driver.find('.test-header-color-select .test-color-select').click();
|
||||
}
|
||||
|
||||
export async function assertHeaderTextColor(col: string, color: string) {
|
||||
await assertTextColor(await getColumnHeader(col), color);
|
||||
}
|
||||
|
||||
export async function assertHeaderFillColor(col: string, color: string) {
|
||||
await assertFillColor(await getColumnHeader(col), color);
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens a cell color picker, either the default one or the one for a specific style rule.
|
||||
*/
|
||||
export function openCellColorPicker(nr?: number) {
|
||||
if (nr !== undefined) {
|
||||
return driver
|
||||
.find(`.test-widget-style-conditional-rule-${nr} .test-color-select`)
|
||||
.find(`.test-widget-style-conditional-rule-${nr} .test-cell-color-select .test-color-select`)
|
||||
.click();
|
||||
}
|
||||
return driver.find('.test-color-select').click();
|
||||
return driver.find('.test-cell-color-select .test-color-select').click();
|
||||
}
|
||||
|
||||
export async function assertCellTextColor(col: string, row: number, color: string) {
|
||||
|
Loading…
Reference in New Issue
Block a user