How to use defaultGlobals method in storybook-root

Best JavaScript code snippet using storybook-root

tmtheme.js

Source:tmtheme.js Github

copy

Full Screen

1var fs = require("fs");2var path = require("path");3var util = require("util");4var cssParse = require("css-parse");5var cssStringify = require("css-stringify");6var parseString = require("plist").parseString;7function parseTheme(themeXml, callback) {8 parseString(themeXml, function(_, theme) {9 callback(theme[0])10 });11}12var unsupportedScopes = { };13var supportedScopes = {14 "keyword": "keyword",15 "keyword.operator": "keyword.operator",16 "keyword.other.unit": "keyword.other.unit",17 "constant": "constant",18 "constant.language": "constant.language",19 "constant.library": "constant.library",20 "constant.numeric": "constant.numeric",21 "constant.character" : "constant.character",22 "constant.character.escape" : "constant.character.escape",23 "constant.character.entity": "constant.character.entity",24 "constant.other" : "constant.other",25 "support": "support",26 "support.function": "support.function",27 "support.function.dom": "support.function.dom",28 "support.function.firebug": "support.firebug",29 "support.function.constant": "support.function.constant",30 "support.constant": "support.constant",31 "support.constant.property-value": "support.constant.property-value",32 "support.class": "support.class",33 "support.type": "support.type",34 "support.other": "support.other",35 "function": "function",36 "function.buildin": "function.buildin",37 "storage": "storage",38 "storage.type": "storage.type",39 "invalid": "invalid",40 "invalid.illegal": "invalid.illegal",41 "invalid.deprecated": "invalid.deprecated",42 "string": "string",43 "string.regexp": "string.regexp",44 "comment": "comment",45 "comment.documentation": "comment.doc",46 "comment.documentation.tag": "comment.doc.tag",47 "variable": "variable",48 "variable.language": "variable.language",49 "variable.parameter": "variable.parameter",50 "meta": "meta",51 "meta.tag.sgml.doctype": "xml-pe",52 "meta.tag": "meta.tag",53 "meta.selector": "meta.selector",54 55 "entity.other.attribute-name": "entity.other.attribute-name",56 "entity.name.function": "entity.name.function",57 "entity.name": "entity.name",58 "entity.name.tag": "entity.name.tag",59 "markup.heading": "markup.heading",60 "markup.heading.1": "markup.heading.1",61 "markup.heading.2": "markup.heading.2",62 "markup.heading.3": "markup.heading.3",63 "markup.heading.4": "markup.heading.4",64 "markup.heading.5": "markup.heading.5",65 "markup.heading.6": "markup.heading.6",66 "markup.list": "markup.list",67 "collab.user1": "collab.user1"68};69var fallbackScopes = {70 "keyword": "meta",71 "support.type": "storage.type",72 "variable": "entity.name.function"73};74// Taken from .ace-tm75var defaultGlobals = {76 "printMargin": "#e8e8e8",77 "background": "#ffffff",78 "foreground": "#000000",79 "gutter": "#f0f0f0",80 "selection": "rgb(181, 213, 255)",81 "step": "rgb(198, 219, 174)",82 "bracket": "rgb(192, 192, 192)",83 "active_line": "rgba(0, 0, 0, 0.07)",84 "cursor": "#000000",85 "invisible": "rgb(191, 191, 191)",86 "fold": "#6b72e6"87};88function extractStyles(theme) {89 var globalSettings = theme.settings[0].settings;90 var colors = {91 "printMargin": defaultGlobals.printMargin,92 "background": parseColor(globalSettings.background) || defaultGlobals.background,93 "foreground": parseColor(globalSettings.foreground) || defaultGlobals.foreground,94 "gutter": defaultGlobals.gutter,95 "selection": parseColor(globalSettings.selection) || defaultGlobals.selection,96 "step": defaultGlobals.step,97 "bracket": parseColor(globalSettings.invisibles) || defaultGlobals.bracket,98 "active_line": parseColor(globalSettings.lineHighlight) || defaultGlobals.active_line,99 "cursor": parseColor(globalSettings.caret) || defaultGlobals.cursor,100 "invisible": "color: " + (parseColor(globalSettings.invisibles) || defaultGlobals.invisible) + ";"101 };102 for (var i=1; i<theme.settings.length; i++) {103 var element = theme.settings[i];104 if (!element.scope || !element.settings)105 continue;106 var scopes = element.scope.split(/\s*[|,]\s*/g);107 for (var j = 0; j < scopes.length; j++) {108 var scope = scopes[j];109 var style = parseStyles(element.settings);110 111 var aceScope = supportedScopes[scope];112 if (aceScope) {113 colors[aceScope] = style;114 }115 else if (style) {116 unsupportedScopes[scope] = (unsupportedScopes[scope] || 0) + 1;117 }118 } 119 }120 121 for (var i in fallbackScopes) {122 if (!colors[i])123 colors[i] = colors[fallbackScopes[i]];124 }125 if (!colors.fold) {126 var foldSource = colors["entity.name.function"] || colors.keyword;127 if (foldSource) {128 colors.fold = foldSource.match(/\:([^;]+)/)[1];129 } else {130 colors.fold = defaultGlobals.fold;131 }132 }133 134 colors.gutterBg = colors.background135 colors.gutterFg = mix(colors.foreground, colors.background, 0.5)136 if (!colors.selected_word_highlight)137 colors.selected_word_highlight = "border: 1px solid " + colors.selection + ";";138 colors.isDark = (luma(colors.background) < 0.5) + "";139 140 return colors;141};142function mix(c1, c2, a1, a2) {143 c1 = rgbColor(c1);144 c2 = rgbColor(c2);145 if (a2 === undefined)146 a2 = 1 - a1147 return "rgb(" + [148 Math.round(a1*c1[0] + a2*c2[0]),149 Math.round(a1*c1[1] + a2*c2[1]),150 Math.round(a1*c1[2] + a2*c2[2])151 ].join(",") + ")";152}153function rgbColor(color) {154 if (typeof color == "object")155 return color;156 if (color[0]=="#")157 return color.match(/^#(..)(..)(..)/).slice(1).map(function(c) {158 return parseInt(c, 16);159 });160 else161 return color.match(/\(([^,]+),([^,]+),([^,]+)/).slice(1).map(function(c) {162 return parseInt(c, 10);163 });164}165function luma(color) {166 var rgb = rgbColor(color);167 return (0.21 * rgb[0] + 0.72 * rgb[1] + 0.07 * rgb[2]) / 255;168}169function parseColor(color) {170 if (!color.length) return null;171 if (color.length == 4)172 color = color.replace(/[a-fA-F\d]/g, "$&$&");173 if (color.length == 7)174 return color;175 else {176 if (!color.match(/^#(..)(..)(..)(..)$/))177 console.error("can't parse color", color);178 var rgba = color.match(/^#(..)(..)(..)(..)$/).slice(1).map(function(c) {179 return parseInt(c, 16);180 });181 rgba[3] = (rgba[3] / 0xFF).toPrecision(2);182 return "rgba(" + rgba.join(", ") + ")";183 }184}185function parseStyles(styles) {186 var css = [];187 var fontStyle = styles.fontStyle || "";188 if (fontStyle.indexOf("underline") !== -1) {189 css.push("text-decoration:underline;");190 }191 if (fontStyle.indexOf("italic") !== -1) {192 css.push("font-style:italic;");193 }194 if (styles.foreground) {195 css.push("color:" + parseColor(styles.foreground) + ";");196 }197 if (styles.background) {198 css.push("background-color:" + parseColor(styles.background) + ";");199 }200 return css.join("\n");201}202function fillTemplate(template, replacements) {203 return template.replace(/%(.+?)%/g, function(str, m) {204 return replacements[m] || "";205 });206}207function hyphenate(str) {208 return str.replace(/([A-Z])/g, "-$1").replace(/_/g, "-").toLowerCase();209}210function quoteString(str) {211 return '"' + str.replace(/\\/, "\\\\").replace(/"/g, '\\"').replace(/\n/g, "\\\n") + '"';212}213var cssTemplate = fs.readFileSync(__dirname + "/templates/theme.css", "utf8");214var jsTemplate = fs.readFileSync(__dirname + "/templates/theme.js", "utf8");215function normalizeStylesheet(rules) {216 for (var i = rules.length; i--; ) {217 var s = JSON.stringify(rules[i].declarations);218 for (var j = i; j --; ) {219 if (s == JSON.stringify(rules[j].declarations)) {220 console.log(rules[j].selectors, rules[i].selectors)221 console.log(i, j)222 rules[j].selectors = rules[i].selectors.concat(rules[j].selectors);223 rules.splice(i, 1);224 break;225 }226 }227 }228 for (var i = rules.length; i--; ) {229 var s = rules[i].selectors.sort();230 rules[i].selectors = s.filter(function(x, i) {231 return x && x != s[i + 1];232 }); 233 }234 return rules;235}236var themes = {237 //"chrome": "Chrome DevTools",238 "clouds": "Clouds",239 "clouds_midnight": "Clouds Midnight",240 "cobalt": "Cobalt",241 //"crimson_editor": "Crimson Editor",242 "dawn": "Dawn",243 //"dreamweaver": "Dreamweaver",244 //"eclipse": "Eclipse",245 //"github": "GitHub",246 "idle_fingers": "idleFingers",247 "kr_theme": "krTheme",248 "merbivore": "Merbivore",249 "merbivore_soft": "Merbivore Soft",250 "mono_industrial": "monoindustrial",251 "monokai": "Monokai",252 "pastel_on_dark": "Pastels on Dark",253 "solarized_dark": "Solarized-dark",254 "solarized_light": "Solarized-light",255 "katzenmilch": "Katzenmilch",256 "kuroir": "Kuroir Theme",257 //"textmate": "Textmate (Mac Classic)",258 "tomorrow": "Tomorrow",259 "tomorrow_night": "Tomorrow-Night",260 "tomorrow_night_blue": "Tomorrow-Night-Blue",261 "tomorrow_night_bright": "Tomorrow-Night-Bright",262 "tomorrow_night_eighties": "Tomorrow-Night-Eighties",263 "twilight": "Twilight",264 "vibrant_ink": "Vibrant Ink",265 "xcode": "Xcode_default"266};267function convertBuiltinTheme(name) {268 return convertTheme(name, __dirname + "/tmthemes/" + themes[name] + ".tmTheme", __dirname + "/../lib/ace/theme");269}270function convertTheme(name, tmThemePath, outputDirectory) {271 console.log("Converting " + name);272 var tmTheme = fs.readFileSync(tmThemePath, "utf8");273 parseTheme(tmTheme, function(theme) {274 var styles = extractStyles(theme);275 styles.cssClass = "ace-" + hyphenate(name);276 styles.uuid = theme.uuid;277 var css = fillTemplate(cssTemplate, styles);278 css = css.replace(/[^\{\}]+{\s*}/g, "");279 280 for (var i in supportedScopes) {281 if (!styles[i])282 continue;283 css += "." + styles.cssClass + " " +284 i.replace(/^|\./g, ".ace_") + "{" + styles[i] + "}";285 }286 // we're going to look for NEW rules in the parsed content only287 // if such a rule exists, add it to the destination file288 // this way, we preserve all hand-modified rules in the <theme>.css rules,289 // (because some exist, for collab1 and ace_indentation_guide290 try {291 var outThemeCss = fs.readFileSync(outputDirectory + "/" + name + ".css");292 var oldRules = cssParse(outThemeCss).stylesheet.rules;293 var newRules = cssParse(css).stylesheet.rules;294 for (var i = 0; i < newRules.length; i++) {295 var newSelectors = newRules[i].selectors;296 for (var j = 0; j < oldRules.length; j++) {297 var oldSelectors = oldRules[j].selectors;298 newSelectors = newSelectors.filter(function(s) {299 return oldSelectors.indexOf(s) == -1;300 })301 if (!newSelectors.length)302 break;303 }304 if (newSelectors.length) {305 newRules[i].selectors = newSelectors;306 console.log("Adding NEW rule: ", newRules[i])307 oldRules.splice(i, 0, newRules[i]);308 }309 }310 311 oldRules = normalizeStylesheet(oldRules);312 313 css = cssStringify({stylesheet: {rules: oldRules}}, { compress: false });314 } catch(e) {315 console.log("Creating new file: " + name + ".css")316 css = cssStringify(cssParse(css), { compress: false });317 }318 319 var js = fillTemplate(jsTemplate, {320 name: name,321 css: 'require("../requirejs/text!./' + name + '.css")', // quoteString(css), //322 cssClass: "ace-" + hyphenate(name),323 isDark: styles.isDark324 });325 fs.writeFileSync(outputDirectory + "/" + name + ".js", js);326 fs.writeFileSync(outputDirectory + "/" + name + ".css", css);327 })328}329if (process.argv.length > 2) {330 var args = process.argv.splice(2);331 if (args.length < 3) {332 console.error("Usage: node tmtheme.js [theme_name, path/to/theme.tmTheme path/to/output/directory]");333 process.exit(1);334 }335 var name = args[0];336 var themePath = args[1];337 var outputDirectory = args[2];338 convertTheme(name, themePath, outputDirectory);339} else {340 for (var name in themes) {341 convertBuiltinTheme(name);342 }343}344if (Object.keys(unsupportedScopes).length > 0) {345 var sortedUnsupportedScopes = {};346 for (var u in unsupportedScopes) {347 var value = unsupportedScopes[u];348 if (sortedUnsupportedScopes[value] === undefined) {349 sortedUnsupportedScopes[value] = [];350 }351 sortedUnsupportedScopes[value].push(u);352 }353 console.log("I found these unsupported scopes:");354 console.log(sortedUnsupportedScopes);355 console.log("It's safe to ignore these, but they may affect your syntax highlighting if your mode depends on any of these rules.");356 console.log("Refer to the docs on ace.ajax.org for information on how to add a scope to the CSS generator.");357}358/*** TODO: generate images for indent guides in node359var indentGuideColor = "#2D2D2D"360var canvas = document.createElement("canvas")361canvas.width = 1; canvas.height = 2;362var ctx = canvas.getContext("2d")363imageData = ctx.getImageData(0,0,1,2)364function getColor(color) {365 ctx.fillStyle = color;366 ctx.fillRect(0,0,1,2);367 return Array.slice(ctx.getImageData(0,0,1,2).data).slice(0,4) 368}369bgColor = getComputedStyle(ace.renderer.scroller).backgroundColor370var a = [].concat(getColor(bgColor), getColor(indentGuideColor));371a.forEach(function(val,i){imageData.data[i] = val})372ctx.putImageData(imageData,0,0)373image = canvas.toDataURL("png")374var rule = "."+ace.renderer.$theme +" .ace_indent-guide {\n\375 background: url(" + image +") right repeat-y;\n\376}"377console.log(rule)378require("ace/lib/dom").importCssString(rule)...

Full Screen

Full Screen

Using AI Code Generation

copy

Full Screen

1import { defaultGlobals } from 'storybook-root-decorator';2import { addParameters } from '@storybook/react';3import { INITIAL_VIEWPORTS } from '@storybook/addon-viewport';4addParameters({5 viewport: {6 },7});8import { addDecorator } from '@storybook/react';9import { withRootDecorator } from 'storybook-root-decorator';10addDecorator(withRootDecorator);11import { defaultGlobals } from 'storybook-root-decorator';12import { addParameters } from '@storybook/react';13import { INITIAL_VIEWPORTS } from '@storybook/addon-viewport';14addParameters({15 viewport: {16 },17});18import { addDecorator } from '@storybook/react';19import { withRootDecorator } from 'storybook-root-decorator';20addDecorator(withRootDecorator);21import { addDecorator } from '@storybook/react';22import { withRootDecorator } from 'storybook-root-decorator';23addDecorator(withRootDecorator);24import { addDecorator } from '@storybook/react';25import { withRootDecorator } from 'storybook-root-decorator';26addDecorator(withRootDecorator);27import { addDecorator } from '@storybook/react';28import { withRootDecorator } from 'storybook-root-decorator';29addDecorator(withRootDecorator);30import { addDecorator } from '@storybook/react';31import { withRootDecorator } from 'storybook-root-decorator';32addDecorator(withRootDecorator);33import { addDecorator } from '@storybook/react';34import { withRootDecorator } from 'storybook-root-decorator';35addDecorator(withRootDecorator);36import { addDecorator } from '@storybook/react';37import { withRootDecorator } from 'storybook-root-decorator';38addDecorator(withRootDecorator);39import { addDecorator } from '@storybook/react';

Full Screen

Using AI Code Generation

copy

Full Screen

1import { defaultGlobals } from 'storybook-root-decorator';2export default {3};4export const Text = () => <button type="button">Hello Button</button>;5export const Emoji = () => (6);7import { withRootDecorator } from 'storybook-root-decorator';8export const decorators = [withRootDecorator];9module.exports = {10 webpackFinal: async (config) => {11 config.module.rules.push({12 loaders: [require.resolve('@storybook/source-loader')],13 });14 return config;15 },16};17import { addons } from '@storybook/addons';18import { themes } from '@storybook/theming';19addons.setConfig({20});21import { withRootDecorator } from 'storybook-root-decorator';22export const decorators = [withRootDecorator];23module.exports = {24 webpackFinal: async (config) => {25 config.module.rules.push({26 loaders: [require.resolve('@storybook/source-loader')],27 });28 return config;29 },30};31import { addons } from '@storybook/addons';32import { themes } from '@storybook/theming';33addons.setConfig({34});35import { configure }

Full Screen

Using AI Code Generation

copy

Full Screen

1import { defaultGlobals } from 'storybook-root-decorator';2import { addDecorator } from '@storybook/react';3addDecorator(defaultGlobals);4import { defaultGlobals } from 'storybook-root-decorator';5import { addDecorator } from '@storybook/react';6addDecorator(defaultGlobals);7import React from 'react';8import { storiesOf } from '@storybook/react';9import { withKnobs, text } from '@storybook/addon-knobs';10import { withInfo } from '@storybook/addon-info';11storiesOf('Button', module)12 .addDecorator(withKnobs)13 .addDecorator(withInfo)14 .add('with text', () => <button>{text('Label', 'Hello Button')}</button>)15 .add('with some emoji', () => (16 <button>{text('Label', 'πŸ˜€ 😎 πŸ‘ πŸ’―')}</button>17 ));18import React from 'react';19import { storiesOf } from '@storybook/react';20import { withKnobs, text } from '@storybook/addon-knobs';21import { withInfo } from '@storybook/addon-info';22storiesOf('Button', module)23 .addDecorator(withKnobs)24 .addDecorator(withInfo)25 .add('with text', () => <button>{text('Label', 'Hello Button')}</button>)26 .add('with some emoji', () => (27 <button>{text('Label', 'πŸ˜€ 😎 πŸ‘ πŸ’―')}</button>28 ));29import React from 'react';30import { storiesOf } from '@storybook/react';31import { withKnobs, text } from '@storybook/addon-knobs';32import { withInfo } from '@storybook/addon-info';33storiesOf('Button', module)34 .addDecorator(withKnobs)35 .addDecorator(withInfo)36 .add('with text', () => <button>{text('Label', 'Hello Button')}</button>)37 .add('with some emoji', () => (38 <button>{text('Label', 'πŸ˜€ 😎 πŸ‘ πŸ’―')}</button>39 ));40import React from 'react';41import { storiesOf } from

Full Screen

Using AI Code Generation

copy

Full Screen

1import { defaultGlobals } from 'storybook-root-decorator';2import { withKnobs } from '@storybook/addon-knobs';3export const decorators = [defaultGlobals, withKnobs];4export const parameters = {5 actions: { argTypesRegex: '^on[A-Z].*' },6};7export const globalTypes = {8 theme: {9 toolbar: {10 },11 },12};13export const globals = {14};15export const globalTypes = {16 theme: {17 toolbar: {18 },19 },20};21export const globals = {22};23export const globalTypes = {24 theme: {25 toolbar: {26 },27 },28};29export const globals = {30};31export const globalTypes = {32 theme: {33 toolbar: {

Full Screen

Using AI Code Generation

copy

Full Screen

1import { defaultGlobals } from 'storybook-root-decorator';2import { addDecorator } from '@storybook/react';3import { withKnobs } from '@storybook/addon-knobs';4addDecorator(withKnobs);5addDecorator(defaultGlobals);6export const decorators = [defaultGlobals];7export const parameters = {8 backgrounds: {9 {10 },11 {12 },13 },14};15export const globalTypes = {16 theme: {17 toolbar: {18 },19 },20};21export const args = {22};23export const argTypes = {24 theme: {25 control: {26 },27 },28};29 async () => {30 const module = await import('./src/components/Component.stories');31 return {32 };33 },34];35 (Story) => (36 <div style={{ padding: '3rem' }}>37];38MIT Β© [sahilrajput03](

Full Screen

Using AI Code Generation

copy

Full Screen

1import { defaultGlobals } from 'storybook-root';2const globals = defaultGlobals();3import { defaultGlobals } from 'storybook-root';4const globals = defaultGlobals();5import { defaultGlobals } from 'storybook-root';6const globals = defaultGlobals();7import { defaultGlobals } from 'storybook-root';8const globals = defaultGlobals();9import { defaultGlobals } from 'storybook-root';10const globals = defaultGlobals();11import { defaultGlobals } from 'storybook-root';12const globals = defaultGlobals();13import { defaultGlobals } from 'storybook-root';14const globals = defaultGlobals();15import { defaultGlobals } from 'storybook-root';16const globals = defaultGlobals();17import { defaultGlobals } from 'storybook-root';18const globals = defaultGlobals();19import { defaultGlobals } from 'storybook-root';20const globals = defaultGlobals();21import { defaultGlobals } from 'storybook-root';22const globals = defaultGlobals();

Full Screen

Using AI Code Generation

copy

Full Screen

1import { defaultGlobals } from 'storybook-root-decorator';2export const parameters = {3};4export const decorators = [defaultGlobals];5export * from 'storybook-root-decorator';6import { defaultGlobals } from 'storybook-root-decorator';7export const parameters = {8};9export const decorators = [defaultGlobals];10export * from 'storybook-root-decorator';11import { defaultGlobals } from 'storybook-root-decorator';12export const parameters = {13};14export const decorators = [defaultGlobals];15export * from 'storybook-root-decorator';16import { defaultGlobals } from 'storybook-root-decorator';17export const parameters = {18};19export const decorators = [defaultGlobals];20export * from 'storybook-root-decorator';21import { defaultGlobals } from 'storybook-root-decorator';22export const parameters = {23};

Full Screen

Using AI Code Generation

copy

Full Screen

1import { defaultGlobals } from 'storybook-root-decorator';2import { addParameters } from '@storybook/react';3addParameters({4});5import { addDecorator } from '@storybook/react';6import { withRootDecorator } from 'storybook-root-decorator';7addDecorator(withRootDecorator);8import { addDecorator } from '@storybook/react';9import { withRootDecorator } from 'storybook-root-decorator';10addDecorator(withRootDecorator);11import { addDecorator } from '@storybook/react';12import { withRootDecorator } from 'storybook-root-decorator';13addDecorator(withRootDecorator);14import { addDecorator } from '@storybook/react';15import { withRootDecorator } from 'storybook-root-decorator';16addDecorator(withRootDecorator);17import { addDecorator } from '@storybook/react';18import { withRootDecorator } from 'storybook-root-decorator';19addDecorator(withRootDecorator);20import { addDecorator } from '@storybook/react';21import { withRootDecorator } from 'storybook-root-decorator';22addDecorator(withRootDecorator);23import { addDecorator } from '@storybook/react';24import { withRootDecorator } from 'storybook-root-decorator';25addDecorator(withRootDecorator);26import { addDecorator } from '@storybook/react';27import { withRootDecorator } from 'storybook-root-decorator';28addDecorator(withRootDecorator);29import { addDecorator } from '@storybook/react';30import { withRootDecorator } from 'storybook-root-decorator';31addDecorator(withRootDecorator);32import { addDecorator } from '@storybook/react';33import { withRootDecorator } from 'storybook-root-decorator';34addDecorator(withRootDecorator);35import { addDecorator } from '@storybook/react';36import { withRootDecorator } from 'storybook-root-decorator';37addDecorator(withRootDecorator);38import { addDecorator

Full Screen

Automation Testing Tutorials

Learn to execute automation testing from scratch with LambdaTest Learning Hub. Right from setting up the prerequisites to run your first automation test, to following best practices and diving deeper into advanced test scenarios. LambdaTest Learning Hubs compile a list of step-by-step guides to help you be proficient with different test automation frameworks i.e. Selenium, Cypress, TestNG etc.

LambdaTest Learning Hubs:

YouTube

You could also refer to video tutorials over LambdaTest YouTube channel to get step by step demonstration from industry experts.

Run storybook-root automation tests on LambdaTest cloud grid

Perform automation testing on 3000+ real desktop and mobile devices online.

Try LambdaTest Now !!

Get 100 minutes of automation test minutes FREE!!

Next-Gen App & Browser Testing Cloud

Was this article helpful?

Helpful

NotHelpful